init: dec-music 项目初始化
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
package ncm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"jsuse.com/dev/dec-music/decrypt"
|
||||
"jsuse.com/dev/dec-music/internal/crypto"
|
||||
)
|
||||
|
||||
const magicHeader = "CTENFDAM"
|
||||
|
||||
var (
|
||||
keyCore = []byte{
|
||||
0x68, 0x7a, 0x48, 0x52, 0x41, 0x6d, 0x73, 0x6f,
|
||||
0x35, 0x6b, 0x49, 0x6e, 0x62, 0x61, 0x78, 0x57,
|
||||
}
|
||||
keyMeta = []byte{
|
||||
0x23, 0x31, 0x34, 0x6C, 0x6A, 0x6B, 0x5F, 0x21,
|
||||
0x5C, 0x5D, 0x26, 0x30, 0x55, 0x3C, 0x27, 0x28,
|
||||
}
|
||||
)
|
||||
|
||||
func newDecoder(p *decrypt.DecoderParams) decrypt.Decoder {
|
||||
return &decoder{rd: p.Reader}
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
rd io.ReadSeeker
|
||||
offset int
|
||||
cipher decrypt.StreamDecoder
|
||||
}
|
||||
|
||||
func (d *decoder) Validate() error {
|
||||
if err := d.validateMagicHeader(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := d.rd.Seek(2, io.SeekCurrent); err != nil {
|
||||
return fmt.Errorf("ncm seek file: %w", err)
|
||||
}
|
||||
|
||||
keyData, err := d.readKeyData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.skipMetaData(); err != nil {
|
||||
return fmt.Errorf("ncm skip meta: %w", err)
|
||||
}
|
||||
|
||||
if _, err := d.rd.Seek(5, io.SeekCurrent); err != nil {
|
||||
return fmt.Errorf("ncm seek gap: %w", err)
|
||||
}
|
||||
|
||||
if err := d.skipCoverData(); err != nil {
|
||||
return fmt.Errorf("ncm skip cover: %w", err)
|
||||
}
|
||||
|
||||
d.cipher = newNcmCipher(keyData)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *decoder) validateMagicHeader() error {
|
||||
header := make([]byte, len(magicHeader))
|
||||
if _, err := d.rd.Read(header); err != nil {
|
||||
return fmt.Errorf("ncm read magic header: %w", err)
|
||||
}
|
||||
if !bytes.Equal([]byte(magicHeader), header) {
|
||||
return errors.New("ncm magic header not match")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *decoder) readKeyData() ([]byte, error) {
|
||||
bKeyLen := make([]byte, 4)
|
||||
if _, err := io.ReadFull(d.rd, bKeyLen); err != nil {
|
||||
return nil, fmt.Errorf("ncm read key length: %w", err)
|
||||
}
|
||||
iKeyLen := binary.LittleEndian.Uint32(bKeyLen)
|
||||
|
||||
bKeyRaw := make([]byte, iKeyLen)
|
||||
if _, err := io.ReadFull(d.rd, bKeyRaw); err != nil {
|
||||
return nil, fmt.Errorf("ncm read key data: %w", err)
|
||||
}
|
||||
for i := uint32(0); i < iKeyLen; i++ {
|
||||
bKeyRaw[i] ^= 0x64
|
||||
}
|
||||
|
||||
return crypto.PKCS7UnPadding(crypto.DecryptAES128ECB(bKeyRaw, keyCore))[17:], nil
|
||||
}
|
||||
|
||||
func (d *decoder) skipMetaData() error {
|
||||
bMetaLen := make([]byte, 4)
|
||||
if _, err := io.ReadFull(d.rd, bMetaLen); err != nil {
|
||||
return fmt.Errorf("ncm read meta length: %w", err)
|
||||
}
|
||||
iMetaLen := binary.LittleEndian.Uint32(bMetaLen)
|
||||
if iMetaLen == 0 {
|
||||
return nil
|
||||
}
|
||||
_, err := d.rd.Seek(int64(iMetaLen), io.SeekCurrent)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *decoder) skipCoverData() error {
|
||||
bCoverFrameLen := make([]byte, 4)
|
||||
if _, err := io.ReadFull(d.rd, bCoverFrameLen); err != nil {
|
||||
return fmt.Errorf("ncm read cover frame length: %w", err)
|
||||
}
|
||||
|
||||
coverFrameStartOffset, err := d.rd.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
coverFrameLen := binary.LittleEndian.Uint32(bCoverFrameLen)
|
||||
|
||||
bCoverLen := make([]byte, 4)
|
||||
if _, err := io.ReadFull(d.rd, bCoverLen); err != nil {
|
||||
return fmt.Errorf("ncm read cover length: %w", err)
|
||||
}
|
||||
iCoverLen := binary.LittleEndian.Uint32(bCoverLen)
|
||||
|
||||
if _, err := io.ReadFull(d.rd, make([]byte, iCoverLen)); err != nil {
|
||||
return fmt.Errorf("ncm skip cover data: %w", err)
|
||||
}
|
||||
|
||||
offsetAudioData := coverFrameStartOffset + int64(coverFrameLen) + 4
|
||||
_, err = d.rd.Seek(offsetAudioData, io.SeekStart)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *decoder) Read(buf []byte) (int, error) {
|
||||
n, err := d.rd.Read(buf)
|
||||
if n > 0 {
|
||||
d.cipher.Decrypt(buf[:n], d.offset)
|
||||
d.offset += n
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func init() {
|
||||
decrypt.RegisterDecoder("ncm", false , newDecoder)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package ncm
|
||||
|
||||
type ncmCipher struct {
|
||||
key []byte
|
||||
box []byte
|
||||
}
|
||||
|
||||
func newNcmCipher(key []byte) *ncmCipher {
|
||||
return &ncmCipher{
|
||||
key: key,
|
||||
box: buildKeyBox(key),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ncmCipher) Decrypt(buf []byte, offset int) {
|
||||
for i := 0; i < len(buf); i++ {
|
||||
buf[i] ^= c.box[(i+offset)&0xff]
|
||||
}
|
||||
}
|
||||
|
||||
func buildKeyBox(key []byte) []byte {
|
||||
box := make([]byte, 256)
|
||||
for i := 0; i < 256; i++ {
|
||||
box[i] = byte(i)
|
||||
}
|
||||
|
||||
var j byte
|
||||
for i := 0; i < 256; i++ {
|
||||
j = box[i] + j + key[i%len(key)]
|
||||
box[i], box[j] = box[j], box[i]
|
||||
}
|
||||
|
||||
ret := make([]byte, 256)
|
||||
var _i byte
|
||||
for i := 0; i < 256; i++ {
|
||||
_i = byte(i + 1)
|
||||
si := box[_i]
|
||||
sj := box[_i+si]
|
||||
ret[i] = box[si+sj]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
Reference in New Issue
Block a user