package binsnoop import ( "encoding/base64" "fmt" "strings" ) // ---- AES descriptions ---- const ( DescriptionAESSBox = "AES S-Box" DescriptionAESFT = "AES Forward Table" DescriptionAESRT = "AES Reverse Table" DescriptionAESRCON = "AES RCON" DescriptionAESAcc = "AES Acceleration Table" ) // aesSBoxMagic returns magic bytes for AES S-Box (forward and reverse). func aesSBoxMagic() [][]byte { return [][]byte{ {0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5}, // Forward S-Box {0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38}, // Reverse S-Box } } // aesSBoxParser validates AES S-Box signatures. func aesSBoxParser(data []byte, offset int) (*SignatureResult, error) { return &SignatureResult{ Offset: offset, Description: DescriptionAESSBox, Confidence: ConfidenceLow, }, nil } // aesForwardTableMagic returns magic bytes for AES Forward Table. func aesForwardTableMagic() [][]byte { return [][]byte{ {0xC6, 0x63, 0x63, 0xA5, 0xF8, 0x7C, 0x7C, 0x84, 0xEE, 0x77, 0x77, 0x99, 0xF6, 0x7B, 0x7B, 0x8D}, } } // aesForwardTableParser validates AES Forward Table signatures. func aesForwardTableParser(data []byte, offset int) (*SignatureResult, error) { return &SignatureResult{ Offset: offset, Description: DescriptionAESFT, Confidence: ConfidenceLow, }, nil } // aesReverseTableMagic returns magic bytes for AES Reverse Table. func aesReverseTableMagic() [][]byte { return [][]byte{ {0x51, 0xF4, 0xA7, 0x50, 0x7E, 0x41, 0x65, 0x53, 0x1A, 0x17, 0xA4, 0xC3, 0x3A, 0x27, 0x5E, 0x96}, } } // aesReverseTableParser validates AES Reverse Table signatures. func aesReverseTableParser(data []byte, offset int) (*SignatureResult, error) { return &SignatureResult{ Offset: offset, Description: DescriptionAESRT, Confidence: ConfidenceLow, }, nil } // aesRconMagic returns magic bytes for AES RCON. func aesRconMagic() [][]byte { return [][]byte{ {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36}, {0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00}, } } // aesRconParser validates AES RCON signatures. func aesRconParser(data []byte, offset int) (*SignatureResult, error) { return &SignatureResult{ Offset: offset, Description: DescriptionAESRCON, Confidence: ConfidenceLow, }, nil } // aesAccelerationTableMagic returns magic bytes for AES Acceleration Table. func aesAccelerationTableMagic() [][]byte { return [][]byte{ // combined sbox x2 {0xA5, 0x84, 0x99, 0x8D, 0x0D, 0xBD, 0xB1, 0x54, 0x50, 0x03, 0xA9, 0x7D, 0x19, 0x62, 0xE6, 0x9A}, // combined sbox x3 {0xC6, 0xF8, 0xEE, 0xF6, 0xFF, 0xD6, 0xDE, 0x91, 0x60, 0x02, 0xCE, 0x56, 0xE7, 0xB5, 0x4D, 0xEC}, // Galois mult 2 {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e}, // Galois mult 3 {0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11, 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39}, // Galois mult 9 {0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf}, // Galois mult 11 {0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69, 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81}, // Galois mult 13 {0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b, 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3}, // Galois mult 14 {0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a, 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca}, } } // aesAccelerationTableParser validates AES Acceleration Table signatures. func aesAccelerationTableParser(data []byte, offset int) (*SignatureResult, error) { return &SignatureResult{ Offset: offset, Description: DescriptionAESAcc, Confidence: ConfidenceLow, }, nil } // ---- RSA ---- const DescriptionRSA = "RSA encrypted session key" type rsaKeyDefinition struct { magic []byte keySize int usageOffset int keyIDOffset int validChecks map[int][][]byte terminatorOffset int } func rsaKeyDefinitions() []rsaKeyDefinition { return []rsaKeyDefinition{ // 1024b RSA key { magic: []byte{0x84, 0x8C, 0x03}, keySize: 1024, keyIDOffset: 3, terminatorOffset: 142, usageOffset: 11, validChecks: map[int][][]byte{ 12: { {0x04, 0x00}, {0x03, 0xff}, {0x03, 0xfe}, {0x03, 0xfd}, {0x03, 0xfc}, {0x03, 0xfb}, {0x03, 0xfa}, {0x03, 0xf9}, }, }, }, // 2048b RSA key { magic: []byte{0x85, 0x01, 0x0c, 0x03}, keySize: 2048, keyIDOffset: 4, terminatorOffset: 271, usageOffset: 12, validChecks: map[int][][]byte{ 13: { {0x08, 0x00}, {0x07, 0xff}, {0x07, 0xfe}, {0x07, 0xfd}, {0x07, 0xfc}, {0x07, 0xfb}, {0x07, 0xfa}, {0x07, 0xf9}, }, }, }, // 3072b RSA key { magic: []byte{0x85, 0x01, 0x8c, 0x03}, keySize: 3072, keyIDOffset: 4, terminatorOffset: 399, usageOffset: 12, validChecks: map[int][][]byte{ 13: { {0x0c, 0x00}, {0x0b, 0xff}, {0x0b, 0xfe}, {0x0b, 0xfd}, {0x0b, 0xfc}, {0x0b, 0xfb}, {0x0b, 0xfa}, {0x0b, 0xf9}, }, }, }, // 4096b RSA key { magic: []byte{0x85, 0x02, 0x0c, 0x03}, keySize: 4096, keyIDOffset: 4, terminatorOffset: 527, usageOffset: 12, validChecks: map[int][][]byte{ 13: { {0x10, 0x00}, {0x0f, 0xff}, {0x0f, 0xfe}, {0x0f, 0xfd}, {0x0f, 0xfc}, {0x0f, 0xfb}, {0x0f, 0xfa}, {0x0f, 0xf9}, }, }, }, // 8192b RSA key { magic: []byte{0x85, 0x04, 0x0c, 0x03}, keySize: 8192, keyIDOffset: 4, terminatorOffset: 1039, usageOffset: 12, validChecks: map[int][][]byte{ 13: { {0x20, 0x00}, {0x1f, 0xff}, {0x1f, 0xfe}, {0x1f, 0xfd}, {0x1f, 0xfc}, {0x1f, 0xfb}, {0x1f, 0xfa}, {0x1f, 0xf9}, }, }, }, } } // rsaMagic returns magic bytes for all known RSA key sizes. func rsaMagic() [][]byte { var magics [][]byte for _, def := range rsaKeyDefinitions() { m := make([]byte, len(def.magic)) copy(m, def.magic) magics = append(magics, m) } return magics } type rsaKeyInfo struct { bits int canSign bool canEncrypt bool keyID int dataSize int } func rsaKeyParse(rawData []byte, def *rsaKeyDefinition) (*rsaKeyInfo, error) { const terminatorByte byte = 0xD2 const encryptOnly byte = 2 const signAndEncrypt byte = 1 const validBytesSize = 2 keyIDFields := []StructField{{"id", "u64"}} keyDataLen := def.terminatorOffset + 1 if len(rawData) < keyDataLen { return nil, fmt.Errorf("insufficient RSA key data") } keyData := rawData[:keyDataLen] if keyData[def.terminatorOffset] != terminatorByte { return nil, fmt.Errorf("invalid RSA terminator byte") } if def.keyIDOffset+8 > len(keyData) { return nil, fmt.Errorf("key ID offset out of bounds") } keyIDParsed, err := Parse(keyData[def.keyIDOffset:], keyIDFields, "big") if err != nil { return nil, fmt.Errorf("failed to parse RSA key ID") } info := &rsaKeyInfo{keyID: keyIDParsed["id"]} info.canEncrypt = keyData[def.usageOffset] == encryptOnly info.canSign = keyData[def.usageOffset] == signAndEncrypt if info.canSign { info.canEncrypt = true } if !info.canSign && !info.canEncrypt { return nil, fmt.Errorf("RSA key cannot sign or encrypt") } for checkOffset, expectedList := range def.validChecks { if checkOffset+validBytesSize > len(keyData) { continue } actual := keyData[checkOffset : checkOffset+validBytesSize] for _, expected := range expectedList { if len(expected) == validBytesSize && actual[0] == expected[0] && actual[1] == expected[1] { info.bits = def.keySize info.dataSize = keyDataLen return info, nil } } } return nil, fmt.Errorf("RSA key validation bytes mismatch") } // rsaParser validates RSA encrypted session key signatures. func rsaParser(data []byte, offset int) (*SignatureResult, error) { result := &SignatureResult{ Offset: offset, Description: DescriptionRSA, Confidence: ConfidenceMedium, } for _, def := range rsaKeyDefinitions() { magicEnd := offset + len(def.magic) if magicEnd > len(data) { continue } match := true for i, b := range def.magic { if data[offset+i] != b { match = false break } } if !match { continue } info, err := rsaKeyParse(data[offset:], &def) if err != nil { break } result.Size = info.dataSize result.Description = fmt.Sprintf("%s, %d bits, can sign: %t, can encrypt: %t, key ID: %#018X", result.Description, info.bits, info.canSign, info.canEncrypt, info.keyID) return result, nil } return nil, fmt.Errorf("invalid RSA signature") } // ---- OpenSSL ---- const DescriptionOpenSSL = "OpenSSL encryption" // opensslMagic returns magic bytes for OpenSSL encrypted files. func opensslMagic() [][]byte { return [][]byte{ []byte("Salted__"), } } func isSaltInvalid(salt int) bool { const saltLen = 8 badByteCount := 0 for i := 0; i < saltLen; i++ { saltByte := byte((salt >> (8 * i)) & 0xFF) if saltByte == 0 || IsPrintableASCII(saltByte) { badByteCount++ } } return badByteCount == saltLen } // opensslParser validates OpenSSL encryption signatures. func opensslParser(data []byte, offset int) (*SignatureResult, error) { sslFields := []StructField{{"magic", "u64"}, {"salt", "u64"}} result := &SignatureResult{ Offset: offset, Description: DescriptionOpenSSL, Confidence: ConfidenceLow, } if offset+StructSize(sslFields) > len(data) { return nil, fmt.Errorf("insufficient OpenSSL data") } sslHeader, err := Parse(data[offset:], sslFields, "big") if err != nil { return nil, fmt.Errorf("failed to parse OpenSSL header") } salt := sslHeader["salt"] if salt == 0 { return nil, fmt.Errorf("OpenSSL salt is zero") } if isSaltInvalid(salt) { return nil, fmt.Errorf("OpenSSL salt is invalid") } if offset == 0 { result.Confidence = ConfidenceMedium } result.Description = fmt.Sprintf("%s, salt: %#X", result.Description, salt) return result, nil } // ---- GPG ---- const DescriptionGPGSigned = "GPG signed file" // gpgSignedMagic returns magic bytes for GPG signed files. func gpgSignedMagic() [][]byte { return [][]byte{ {0xA3, 0x01}, } } // gpgSignedParser validates GPG signed file signatures. // The Rust version does a zlib decompression dry run; here we do a simplified validation. func gpgSignedParser(data []byte, offset int) (*SignatureResult, error) { result := &SignatureResult{ Offset: offset, Confidence: ConfidenceHigh, Description: DescriptionGPGSigned, } // GPG signed files are zlib compressed with magic bytes replaced. // Validate there is enough data and the zlib-like stream follows. if offset+4 > len(data) { return nil, fmt.Errorf("insufficient GPG data") } // The third byte should be a valid zlib CMF byte (deflate method = 8) cmf := data[offset+2] if cmf&0x0F != 8 { return nil, fmt.Errorf("GPG: invalid deflate method in underlying zlib stream") } result.Description = fmt.Sprintf("%s, compressed data at offset %d", result.Description, offset) return result, nil } // ---- PEM ---- const ( DescriptionPEMCertificate = "PEM certificate" DescriptionPEMPublicKey = "PEM public key" DescriptionPEMPrivateKey = "PEM private key" ) // pemCertificateMagic returns magic bytes for PEM certificates. func pemCertificateMagic() [][]byte { return [][]byte{ []byte("-----BEGIN CERTIFICATE-----"), } } // pemPublicKeyMagic returns magic bytes for PEM public keys. func pemPublicKeyMagic() [][]byte { return [][]byte{ []byte("-----BEGIN PUBLIC KEY-----"), []byte("-----BEGIN RSA PUBLIC KEY-----"), []byte("-----BEGIN DSA PUBLIC KEY-----"), []byte("-----BEGIN ECDSA PUBLIC KEY-----"), } } // pemPrivateKeyMagic returns magic bytes for PEM private keys. func pemPrivateKeyMagic() [][]byte { return [][]byte{ []byte("-----BEGIN PRIVATE KEY-----"), []byte("-----BEGIN EC PRIVATE KEY-----"), []byte("-----BEGIN RSA PRIVATE KEY-----"), []byte("-----BEGIN DSA PRIVATE KEY-----"), []byte("-----BEGIN OPENSSH PRIVATE KEY-----"), []byte("-----BEGIN ANY PRIVATE KEY-----"), []byte("-----BEGIN ENCRYPTED PRIVATE KEY-----"), []byte("-----BEGIN TSS2 PRIVATE KEY-----"), } } func decodePEMData(pemData []byte) (int, error) { const delim = "--" pemStr := string(pemData) delimCount := 0 var base64Str strings.Builder for _, line := range strings.Split(pemStr, "\n") { line = strings.TrimRight(line, "\r") if strings.HasPrefix(line, delim) { delimCount++ if delimCount == 2 { break } continue } base64Str.WriteString(line) } if base64Str.Len() == 0 { return 0, fmt.Errorf("no PEM base64 data found") } decoded, err := base64.StdEncoding.DecodeString(base64Str.String()) if err != nil { return 0, fmt.Errorf("PEM base64 decode failed: %v", err) } return len(decoded), nil } // pemFindEnd locates the end delimiter and returns the total PEM size. func pemFindEnd(data []byte, offset int) (int, bool) { const endPrefix = "-----END " pemData := string(data[offset:]) idx := strings.Index(pemData, endPrefix) if idx < 0 { return 0, false } // Find end of the delimiter line rest := pemData[idx:] nlIdx := strings.Index(rest, "\n") if nlIdx < 0 { return idx + len(rest), true } return idx + nlIdx + 1, true } // pemParser validates PEM certificate and key signatures. func pemParser(data []byte, offset int) (*SignatureResult, error) { const minPEMLen = 26 result := &SignatureResult{ Offset: offset, Confidence: ConfidenceHigh, } if offset+minPEMLen > len(data) { return nil, fmt.Errorf("insufficient PEM data") } pemMagic := data[offset : offset+minPEMLen] pemMagicStr := string(pemMagic) // Determine type based on first 26 bytes matched := false for _, m := range pemPublicKeyMagic() { if len(m) >= minPEMLen && strings.HasPrefix(string(m), pemMagicStr[:minPEMLen]) { result.Description = DescriptionPEMPublicKey matched = true break } } if !matched { for _, m := range pemPrivateKeyMagic() { if len(m) >= minPEMLen && strings.HasPrefix(string(m), pemMagicStr[:minPEMLen]) { result.Description = DescriptionPEMPrivateKey matched = true break } } } if !matched { for _, m := range pemCertificateMagic() { if len(m) >= minPEMLen && strings.HasPrefix(string(m), pemMagicStr[:minPEMLen]) { result.Description = DescriptionPEMCertificate matched = true break } } } if !matched { return nil, fmt.Errorf("PEM magic not recognised") } // Find the end of PEM data pemSize, found := pemFindEnd(data, offset) if !found || pemSize == 0 { return nil, fmt.Errorf("PEM end delimiter not found") } // Validate base64 content if offset+pemSize > len(data) { pemSize = len(data) - offset } if _, err := decodePEMData(data[offset : offset+pemSize]); err != nil { return nil, fmt.Errorf("PEM validation failed: %v", err) } if offset == 0 && pemSize == len(data) { result.ExtractionDeclined = true } result.Size = pemSize return result, nil } // ---- PKCS DER ---- const DescriptionDERHash = "PKCS DER hash" func derHashLookups() map[string][]byte { return map[string][]byte{ "MD5": {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10}, "SHA1": {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}, "SHA256": {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}, "SHA384": {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}, "SHA512": {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04}, } } // derHashMagic returns magic bytes for all DER hash types. func derHashMagic() [][]byte { var magics [][]byte for _, m := range derHashLookups() { mc := make([]byte, len(m)) copy(mc, m) magics = append(magics, mc) } return magics } // derHashParser validates PKCS DER hash signatures. func derHashParser(data []byte, offset int) (*SignatureResult, error) { result := &SignatureResult{ Offset: offset, Description: DescriptionDERHash, Confidence: ConfidenceMedium, } for name, magic := range derHashLookups() { hashEnd := offset + len(magic) if hashEnd > len(data) { continue } match := true for i, b := range magic { if data[offset+i] != b { match = false break } } if match { result.Size = len(magic) result.Description = fmt.Sprintf("%s, %s", result.Description, name) return result, nil } } return nil, fmt.Errorf("DER hash validation failed") } // ---- LUKS ---- const DescriptionLUKS = "LUKS header" // luksMagic returns magic bytes for LUKS headers. func luksMagic() [][]byte { return [][]byte{ {0x4C, 0x55, 0x4B, 0x53, 0xBA, 0xBE}, // "LUKS\xBA\xBE" } } // luksParser validates LUKS header signatures. func luksParser(data []byte, offset int) (*SignatureResult, error) { const cipherAlgoStart = 8 const cipherAlgoEnd = 40 const cipherModeStart = 40 const cipherModeEnd = 72 const hashFnStart = 72 const hashFnEnd = 104 const luks2MinHeaderSize = 4032 luksBaseFields := []StructField{ {"magic_1", "u32"}, {"magic_2", "u16"}, {"version", "u16"}, {"header_size", "u64"}, } result := &SignatureResult{ Offset: offset, Name: "luks", Description: DescriptionLUKS, Confidence: ConfidenceMedium, } if offset+StructSize(luksBaseFields) > len(data) { return nil, fmt.Errorf("insufficient LUKS data") } luksBase, err := Parse(data[offset:], luksBaseFields, "big") if err != nil { return nil, fmt.Errorf("failed to parse LUKS header") } version := luksBase["version"] // Hash function string if offset+hashFnEnd > len(data) { return nil, fmt.Errorf("LUKS hash function data out of bounds") } hashFn := GetCString(data[offset+hashFnStart : offset+hashFnEnd]) if hashFn == "" { return nil, fmt.Errorf("LUKS hash function string empty") } if version == 1 { if offset+cipherAlgoEnd > len(data) { return nil, fmt.Errorf("LUKS cipher data out of bounds") } cipherAlgo := GetCString(data[offset+cipherAlgoStart : offset+cipherAlgoEnd]) cipherMode := GetCString(data[offset+cipherModeStart : offset+cipherModeEnd]) if cipherAlgo == "" || cipherMode == "" { return nil, fmt.Errorf("LUKS v1 cipher strings empty") } result.Description = fmt.Sprintf("%s, version: %d, cipher algorithm: %s, cipher mode: %s, hash fn: %s", result.Description, version, cipherAlgo, cipherMode, hashFn) return result, nil } else if version == 2 { headerSize := luksBase["header_size"] availableData := len(data) - offset if headerSize <= luks2MinHeaderSize || headerSize >= availableData { return nil, fmt.Errorf("LUKS v2 header size invalid") } result.Description = fmt.Sprintf("%s, version: %d, header size: %d bytes, hash fn: %s", result.Description, version, headerSize, hashFn) return result, nil } return nil, fmt.Errorf("unsupported LUKS version: %d", version) } // ---- Hash constants ---- const hashMagicLen = 16 const ( DescriptionCRC32 = "CRC32 polynomial table" DescriptionSHA256 = "SHA256 hash constants" DescriptionMD5 = "MD5 hash constants" ) // crc32Magic returns magic bytes for CRC32 polynomial tables. // Order matters: index 0 is big endian, index 1 is little endian. func crc32ConstMagic() [][]byte { return [][]byte{ // Big endian {0x00, 0x00, 0x00, 0x00, 0x77, 0x07, 0x30, 0x96, 0xEE, 0x0E, 0x61, 0x2C, 0x99, 0x09, 0x51, 0xBA}, // Little endian {0x00, 0x00, 0x00, 0x00, 0x96, 0x30, 0x07, 0x77, 0x2C, 0x61, 0x0E, 0xEE, 0xBA, 0x51, 0x09, 0x99}, } } // sha256Magic returns magic bytes for SHA256 hash constants. func sha256ConstMagic() [][]byte { return [][]byte{ // Big endian {0x42, 0x8a, 0x2f, 0x98, 0x71, 0x37, 0x44, 0x91, 0xb5, 0xc0, 0xfb, 0xcf, 0xe9, 0xb5, 0xdb, 0xa5}, // Little endian {0x98, 0x2f, 0x8a, 0x42, 0x91, 0x44, 0x37, 0x71, 0xcf, 0xfb, 0xc0, 0xb5, 0xa5, 0xdb, 0xb5, 0xe9}, } } // md5Magic returns magic bytes for MD5 hash constants. func md5ConstMagic() [][]byte { return [][]byte{ // Big endian {0xd7, 0x6a, 0xa4, 0x78, 0xe8, 0xc7, 0xb7, 0x56, 0x24, 0x20, 0x70, 0xdb, 0xc1, 0xbd, 0xce, 0xee}, // Little endian {0x78, 0xa4, 0x6a, 0xd7, 0x56, 0xb7, 0xc7, 0xe8, 0xdb, 0x70, 0x20, 0x24, 0xee, 0xce, 0xbd, 0xc1}, } } func hashEndianness(data []byte, offset int, magics [][]byte) string { if offset+hashMagicLen > len(data) { return "little" } thisMagic := data[offset : offset+hashMagicLen] if len(magics) > 0 && len(magics[0]) == hashMagicLen { match := true for i := 0; i < hashMagicLen; i++ { if thisMagic[i] != magics[0][i] { match = false break } } if match { return "big" } } return "little" } // crc32Parser validates CRC32 polynomial table signatures. func crc32Parser(data []byte, offset int) (*SignatureResult, error) { return &SignatureResult{ Offset: offset, Size: hashMagicLen, Description: fmt.Sprintf("%s, %s endian", DescriptionCRC32, hashEndianness(data, offset, crc32ConstMagic())), }, nil } // sha256Parser validates SHA256 hash constant signatures. func sha256Parser(data []byte, offset int) (*SignatureResult, error) { return &SignatureResult{ Offset: offset, Size: hashMagicLen, Description: fmt.Sprintf("%s, %s endian", DescriptionSHA256, hashEndianness(data, offset, sha256ConstMagic())), }, nil } // md5Parser validates MD5 hash constant signatures. func md5Parser(data []byte, offset int) (*SignatureResult, error) { return &SignatureResult{ Offset: offset, Size: hashMagicLen, Description: fmt.Sprintf("%s, %s endian", DescriptionMD5, hashEndianness(data, offset, md5ConstMagic())), }, nil }