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) }