temari

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 4 Imported by: 0

README

temari — Go package (purego, no cgo)

中文版 README.md

temari calls the Temari cdylib (libtemari.so / .dylib / temari.dll) from pure Go and decrypts Apple Music FairPlay SAMPLE-AES samples directly inside a Go process.

This package does no networking: the template is fetched by the caller (e.g. pull the 40020 JSON response body with net/http) and handed to FromJSON for parsing.

No cgo required: all calls go through purego, so CGO_ENABLED=0 builds, tests, and runs work on Linux, macOS, and Windows.

Usage

# first build the cdylib
cd <temari repo root>
cargo build --release        # artifact target/release/libtemari.so

# run examples and tests (CGO_ENABLED=0 works)
cd bindings/go
go run ./examples/json_decrypt.go      # auto-starts a mock 40020 (caller fetches JSON)
go run ./examples/json_decrypt.go --no-mock --json <template.json>   # offline
go test ./... -v
import "temari"

lib, err := temari.Load("/path/to/target/release/libtemari.so")

// 1) caller owns networking: fetch the 40020-style JSON response body (or read a local JSON file)
//    body, _ := fetchJSON(server, adamID, uri)   // net/http

// 2) the library only parses JSON -> template
t, err := lib.FromJSON(body)

plain, err := t.Decrypt(sample)          // single sample (equal-length plaintext)
plains, err := t.DecryptPar(samples)     // parallel batch, order preserved

t.Close()                                // free the handle

// streaming: submit samples as they arrive, receive in submission order
s, _ := t.NewStream(256)
s.Submit(chunk1); s.Submit(chunk2); s.Finish()
for plain := range s.C() { ... }          // goroutine + channel async
s.Close()

Notes

  • Symbols are bound at Load time; a missing symbol returns an error.
  • Handles must be released with Close().
  • Batch decryption treats each sample as an independent SAMPLE-AES unit (state resets per sample); clients should split samples on fragment/stripe boundaries.

Mapping to the FFI

Go API FFI export
Library.FromJSON tmpl_from_json
Temari.Decrypt decrypt_sample_ffi
Temari.DecryptPar decrypt_samples_par
Temari.NewStream / Stream stream_new/stream_submit/stream_next/...
Temari.Close tmpl_destroy

Documentation

Overview

Package temari wraps the Temari cdylib (libtemari.so / .dylib / temari.dll) with a cgo-free FFI. No C toolchain is required: the package builds and runs with CGO_ENABLED=0, for static binaries and cross-compilation.

The library performs **no network requests**: construct a template from a 40020-style key-server JSON response body with FromJSON — fetch the JSON yourself (own HTTP client) and pass it here.

Loading is platform-specific (see loader_unix.go / loader_windows.go):

  • Linux / macOS / FreeBSD: purego `Dlopen` to open the library
  • Windows: `golang.org/x/sys/windows` `LoadDLL` to open the library (purego supports Windows, but has no `Dlopen` there)

On every platform symbols are bound with purego `RegisterLibFunc` (its loadSymbol is dlopen/dlsym on unix, GetProcAddress on Windows), and all calls go through purego's cgo-free calling convention.

Build the library first:

cd <temari repo> && cargo build --release   # -> libtemari.so / temari.dll

Then load it once:

lib, err := temari.Load("/path/to/libtemari.so")   // or temari.dll on Windows
t, err := lib.FromJSON(jsonBody)                    // caller fetched the JSON
plain, err := t.Decrypt(sample)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Library

type Library struct {
	// contains filtered or unexported fields
}

Library is the loaded temari cdylib. Call Load once and reuse the returned Library. `handle` is the module handle (dlopen on unix; LoadLibrary on Windows).

func Load

func Load(path string) (*Library, error)

Load opens the temari cdylib from path. Returns an error if the library or any required symbol is missing.

func (*Library) FromJSON

func (l *Library) FromJSON(json []byte) (*Temari, error)

FromJSON builds a template handle from a 40020-style key-server JSON response body. The library performs no network requests — fetch the JSON yourself (e.g. with net/http) and pass the raw body here.

type Stream

type Stream struct {
	// contains filtered or unexported fields
}

Stream is an incremental parallel decryptor with in-order results.

Submit encrypted samples as they arrive, then receive plaintexts in submission order. Blocking at the library level; wrap with C() for asynchronous consumption (goroutine + channel).

func (*Stream) C

func (s *Stream) C() <-chan []byte

C returns a channel that receives plaintexts in order until the stream is closed (the channel is then closed). A pump goroutine does the blocking Next() calls off the caller's goroutine.

func (*Stream) Close

func (s *Stream) Close()

Close destroys the stream handle (idempotent).

func (*Stream) Finish

func (s *Stream) Finish()

Finish closes the input side; already-submitted samples still drain.

func (*Stream) Next

func (s *Stream) Next() (plain []byte, ok bool)

Next blocks for the next plaintext (in order). ok=false once the stream is closed and everything is consumed.

func (*Stream) Submit

func (s *Stream) Submit(sample []byte) error

Submit queues one encrypted sample (blocks on internal backpressure).

func (*Stream) TryNext

func (s *Stream) TryNext() (plain []byte, state StreamState)

TryNext is a non-blocking probe.

type StreamState

type StreamState int

StreamState is the result of a non-blocking Stream.TryNext.

const (
	// StreamData: a plaintext is ready.
	StreamData StreamState = iota
	// StreamEmpty: no plaintext pending yet (stream still open).
	StreamEmpty
	// StreamClosed: the stream is closed and consumed.
	StreamClosed
)

type Temari

type Temari struct {
	// contains filtered or unexported fields
}

Temari is an opaque decryption template handle. Thread-safe for concurrent Decrypt calls; free with Close.

func (*Temari) Close

func (t *Temari) Close()

Close frees the template handle (nil-safe, idempotent).

func (*Temari) Decrypt

func (t *Temari) Decrypt(sample []byte) ([]byte, error)

Decrypt decrypts one sample, returning equal-length plaintext.

func (*Temari) DecryptPar

func (t *Temari) DecryptPar(samples [][]byte) ([][]byte, error)

DecryptPar decrypts a batch of independent samples in parallel, preserving order. Each sample is an independent SAMPLE-AES unit (state resets per sample), so a whole stream can be split at fragment boundaries and decrypted across all cores.

Default fast path: samples are read via scattered pointers (no input join memcpy) by decrypt_samples_par; plaintexts land in one flat buffer which is sliced into the returned views.

func (*Temari) NewStream

func (t *Temari) NewStream(batchSize int) (*Stream, error)

NewStream creates a streaming decryptor over this template. The stream clones the template, so the Temari handle may be closed afterwards. batchSize <= 0 selects a default of 256.

Directories

Path Synopsis
bench — temari Go 绑定(purego)性能基准, Linux / Windows 通用。
bench — temari Go 绑定(purego)性能基准, Linux / Windows 通用。
json_decrypt.go — temari Go 包完整示例。
json_decrypt.go — temari Go 包完整示例。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL