SAORI over DirectSSTP¶
SAORI over DirectSSTP¶
参考:SAORI/1.0(外部サイト)
SAORI/1.0の要求を、DLLのrequest関数ではなくDirect SSTPでベースウェアに送り、ベースウェアに内蔵された機能(内蔵モジュール)をSAORIとして呼び出すための拡張規格です。[SSP 2.9.13]
SHIORIから見ると「DLLを置かずに使えるSAORI」になります。ハッシュ・暗号・乱数・日時の計算など、これまでゴーストごとにDLLを同梱していた機能をベースウェアが提供します。
このページの前半(SAORI/1.0の概要~送信側の手順)はベースウェアに依存しない規格、後半(内蔵モジュール)はSSPが内蔵しているモジュールの説明です。
SAORI/1.0の概要¶
SAORI/1.0の正しい定義は原典(外部サイト)を参照してください。ここではこのページを読むのに必要な部分だけを簡単にまとめます。
要求¶
EXECUTE SAORI/1.0
Charset: UTF-8
Sender: 呼び出し元の名前
SecurityLevel: Local
Argument0: 1つ目の引数
Argument1: 2つ目の引数
改行はCR+LF、空行で終わります。引数はArgument0から番号順に並べます。値が空の引数も、その位置の引数として数えます。
Charsetは要求の文字コードで、応答も同じ文字コードで返ります。
機能の有無の確認にはGET Version SAORI/1.0を使います。
応答¶
SAORI/1.0 200 OK
Charset: UTF-8
Result: 結果
Value0: 複数の値を返すときの1つ目
- 200 OK: 成功。
Resultが結果。複数の値を返すときはValue0、Value1…にも入る。 - 204 No Content: 成功したが、返す値が無い(結果が空文字列)。
- 400 Bad Request: 呼び出し方の誤り。
- 500 Internal Server Error: 処理の失敗。
SAORI over DirectSSTP¶
送り方¶
Direct SSTPと同じくWM_COPYDATA(dwData = 9801)を使い、lpDataにSSTPの要求の代わりにSAORI/1.0の要求を入れて、自分のゴーストの窓(FMOのhwnd)へ送ります。
窓・WM_COPYDATAの扱い(SendMessage系で送る、cbDataなど)はDirect SSTPと同じです。
EXECUTE SAORI/1.0
Module: internal_crypto
Charset: UTF-8
Sender: YAYA
SecurityLevel: Local
HWnd: 1234
Argument0: Digest
Argument1: sha256
Argument2: abc
- Module: 【必須】呼び出すモジュールの名前。DLL名から
.dllを除いた形で書く(例:internal_crypto、string)。大文字小文字は区別しない。
無ければ400(ErrorDescription: invalid parameter)、知らないモジュールなら400(ErrorDescription: unknown module)。 - HWnd: 返事を受ける窓。省略時は
WM_COPYDATAのwParamの窓(Direct SSTPのSSTPと同じ規則)。 - Charset: 要求の文字コード。応答も同じ文字コードで返る。
- SecurityLevel: Direct SSTPでは常に
Localとして扱う(ヘッダの値は見ない)。 - Sender: 呼び出し元の名前。ベースウェアは使わない。
- Argument[n]: 引数。SAORI/1.0と同じ。
応答の受け取り¶
応答はHWnd(またはwParam)の窓へ、WM_COPYDATA(dwData = 9801)で返ります。内容はSAORI/で始まるSAORI/1.0の応答です。
ベースウェアは要求の処理中に返事を送るので、送信側のSendMessageが戻る前に返事が届きます。
受け付ける経路¶
SAORI over DirectSSTPはDirect SSTPだけで受け付けます。
ソケットのSSTPやSSTP over HTTPでSAORI/1.0の要求を送ると、SAORI/1.0 400 Bad RequestとErrorDescription: direct sstp onlyが返ります(SSTP over HTTPでは、HTTPの200の本文としてこの応答が返ります)。
SAORIの要求はSSTPログに記録されません。
呼び出し元のゴースト¶
ゴーストの窓へ送った場合は、そのゴーストが呼び出し元になります。
ベースウェアの主窓へ送ったときなど、呼び出し元のゴーストが分からない場合は「ゴーストなし」として扱います(Direct SSTPのSSTPのように表示中のゴーストを代わりに当てることはしません)。 ゴーストを使わない処理は普通に動き、ゴーストに依存する処理(結果をイベントで届ける非同期の呼び出し、ゴーストのフォルダを基準にした相対パスなど)は400(ErrorDescription: ghost required)になります。
ReceiverGhostName / ReceiverGhostHWndヘッダは見ません。
返事の取り違えを防ぐ¶
ベースウェアは、SAORI/(およびSSTP/)で始まる受信を返事とみなし、要求としては扱いません。
機能の判定¶
モジュールが使えるかどうかは、GET Version SAORI/1.0にModuleを付けて送って確かめます。使えればSAORI/1.0 200 OK(SSPではResult: SSP Internal)が返ります。
この拡張に対応していないSSP(2.9.12以前)はSAORI/1.0の要求をSSTPの要求として読み、SSTPのエラー(GET VersionにはSSTP/1.0 501 Not Implemented、EXECUTEにはSSTP/1.0 400 Bad Request)を返します。
送信側は「SAORI/1.0 200で始まる返事でなければ使えない」「返事が来なければ使えない」と判定してください。
SSPの内蔵モジュールとその関数の一覧は、internal_systemで取れます。
ErrorDescription¶
失敗したときは、応答にErrorDescriptionヘッダで失敗理由(固定の英語の文字列)が付きます。SAORI/1.0の原典には無いヘッダで、SHIORI/3.0の同名のヘッダにならっています。
どのモジュールでも使うものは次のとおりです。ほかは各モジュールの説明を参照してください。
- direct sstp only: 400。Direct SSTP以外の経路で送った。
- unknown module: 400。
Moduleのモジュールが無い。 - unknown function: 400。関数名(
Argument0)がモジュールに無い。 - ghost required: 400。呼び出し元のゴーストが分からないのに、ゴーストに依存する処理を求めた。
- invalid parameter: 400。
Moduleの欠け・引数の欠けや誤り。
互換モジュール(DLLのSAORIの互換)は、原作どおりErrorDescriptionの付かない400を返すことがあります。
ヘッダの値に書けないもの¶
SAORIのヘッダの値には、改行と、先頭・末尾の空白を書けません(カンマは書けます)。こうしたデータは、内蔵モジュールでは--data-in=base64などの形式を使って渡してください。
非同期の呼び出し¶
内蔵モジュール(名前がinternal_で始まるもの)の関数は、オプション--async=識別子を付けると非同期で動きます。要求を受け付けると、応答はすぐに204で返り、結果はあとで呼び出し元のゴーストへイベントで届きます。
ゴーストなしの呼び出しでは使えません(400 ghost required)。識別子が空なら400(invalid parameter)です。
時間のかかる処理(大きなファイルのハッシュ、パスワードのハッシュ、鍵の導出、RSAの鍵ペアの生成など)には--asyncを使ってください。
同時に動く非同期の呼び出しは、SSPではCPUの数ほど(2~4)までです。それを超えた分は、前のものが終わるまで順番を待ちます。
結果を届けるイベント¶
- イベント名: 識別子が
Onで始まれば、成功時はその名前、失敗時は「識別子+Failure」。
そうでなければ、成功時はOnInternalSaoriComplete、失敗時はOnInternalSaoriFailure。 - 成功時のReference: Reference0=識別子、Reference1=結果(同期の呼び出しの
Result)、Reference2=モジュール名、Reference3=関数名、Reference4以降=Value0以降。 - 失敗時のReference: Reference0=識別子、Reference1=失敗理由(同期の呼び出しの
ErrorDescriptionと同じ文字列)、Reference2=モジュール名、Reference3=関数名。
引数の数やオプションの名前の誤りは、受付の時点で400になります。引数の中身の誤り(読めない16進など)は、失敗のイベントで届きます。
結果が届く前に呼び出し元のゴーストが終了した場合(SHIORIの再読み込みを含む)、結果は届きません。
送信側の手順¶
SHIORIなどの送信側は、次の手順でSAORI over DirectSSTPを使います。
- FMOなどから、自分のゴーストの窓(
hwnd)を得る。 - 返事を受ける窓を用意する。
- SAORI/1.0の要求を組み立てて、
ModuleとHWnd(返事を受ける窓)を付ける。 - ゴーストの窓へ
WM_COPYDATA(dwData = 9801)をSendMessage系の関数で送る。 - 返事を受ける窓に
WM_COPYDATA(dwData = 9801)で届いた、SAORI/で始まるデータを応答として読む。
使えるかどうかは、最初にGET Versionで確かめてください。
記述例¶
YAYA・里々では、上の手順をSHIORI自身が行います。DLLのSAORIと同じ書き方で、モジュール名をinternal_で始まる名前にするだけです。使えない場合(旧SSPなど)の扱いは、各SHIORIの説明を参照してください。
-
YAYA(
FUNCTIONEX):// 同期 _hash = FUNCTIONEX('internal_crypto', 'Digest', 'sha256', 'abc') // 非同期:結果は OnHashDone に届く(reference[0]=識別子、reference[1]=結果) FUNCTIONEX('internal_crypto', 'HashPassword', 'secret', '--async=OnHashDone') OnHashDone { _phc = reference[1] }
-
里々(登録は不要で、モジュール名をそのまま
()で呼ぶ):*OnBoot (internal_crypto、Digest、sha256、abc) (internal_crypto、HashPassword、secret、--async=OnHashDone) *OnHashDone (R1)
結果がResult、追加の値がValue0以降として扱われるのは、DLLのSAORIと同じです。--asyncを付けた呼び出しの戻り値は空になります。
内蔵モジュールの共通規約¶
SSPが内蔵している、名前がinternal_で始まるモジュールの共通の決まりです。(同じ名前のSAORI DLLと衝突しないように、internal_で始まる名前にしています。)
これらのモジュールはDirect SSTPからだけ呼べます。DLLとしてloadすることはできません。
関数と引数¶
Argument0が関数名(大文字小文字は区別しない)、Argument1以降がその関数の引数です。知らない関数名は400(unknown function)です。
引数は、関数ごとに決まった数の位置引数と、その後ろに並べるオプション(--名前=値)からなります。
- 位置引数の位置にある値は、
--で始まっていてもオプションとしては読まず、データとして扱います。 - オプションは位置引数より後ろにだけ書けます。省略できる位置引数を飛ばしてオプションを書くときは、空の引数を置いてください。
- オプションの名前は大文字小文字を区別しません。同じ名前を2回書くと後のものが有効です。
- 位置引数より後ろにある
--で始まらない値、関数が受け付けないオプションは400(invalid parameter)です。 - [SSP 2.9.25] 位置引数の数が決まっていない関数(internal_fileのMediaInfo)は、2番目以降の位置で最初に
--で始まる値から後ろをオプションとして読みます。 - どの関数も
--async=識別子を受け付けます。
Argument0: Digest
Argument1: sha256
Argument2: SGVsbG8=
Argument3: --data-in=base64
Argument4: --out=base64
バイト列の入力の形式¶
ハッシュなどはバイト列を扱いますが、SAORIで渡せるのは文字列です。バイト列を受け取る引数にはそれぞれ名前(data、key、password、salt、ciphertext、signature、a、bなど。各関数の説明を参照)があり、--名前-in=形式でその引数の読み方を変えられます。例:--key-in=hex、--data-in=base64。
オプションで渡すバイト列(--aad=、--iv=、--info=)も同じで、--aad-in=hexのように読み方を変えられます。
- text: 文字列を、
--charset=の文字コード(既定はUTF-8)のバイト列にする。要求のCharsetではない(送り手の文字コードによらず、Web APIの署名などと同じ値になるように)。その文字コードで表せない文字があれば400(invalid encoding)。 - hex: 16進。大文字小文字はどちらでもよい。奇数桁・16進以外の文字は400(
invalid encoding)。 - base64 / base64url: パディング(
=)はあってもなくてもよい。どちらの形式でも、base64とbase64urlの両方の文字(+ / - _)を読む。それ以外の文字・パディングの誤りは400(invalid encoding)。 - base32: RFC 4648。大文字小文字・パディングの有無は問わない。不正な文字は400(
invalid encoding)。 - url: パーセントエンコードを戻したもの。
+は空白に戻さない。%の後ろが16進2桁でなければ400(invalid encoding)。 - bech32: [SSP 2.9.14] BIP-173のbech32(nostrの
npub1.../nsec1...など。bech32mは読めない)。データの部分をバイト列にする。1より前の部分(hrp。npubなど)は見ないので、種類を確かめたいときはスクリプトで文字列の先頭を見ること。大文字だけ・小文字だけのどちらでもよい。混在・チェックサムの誤り・不正な文字は400(invalid encoding)。長さ(BIP-173の90文字)の制限はない。 - file: 値をファイルのパスとして、ファイルの中身を読む。
internal_cryptoのDigest/HMACのdataだけで使える。
相対パスはゴーストのフォルダ(ghost/master)が基準。ゴーストなしの呼び出しで相対パスを渡すと400(ghost required)。ファイルが無ければ400(file not found)、途中で読めなくなったら500(file read failed)。
既定の形式は引数ごとに決まっています(各関数の説明を参照)。デコードした後のデータは1つの引数あたり8MBまで(too large)ですが、Direct SSTPの要求そのものが1MBまでなので、実際にはそれより小さくなります。fileは少しずつ読むので大きさの上限はありません。
バイト列の出力の形式¶
バイト列を返す関数は、--out=形式で結果の書き方を変えられます。既定の形式は関数ごとに決まっています。出力の既定の形式は、同じ名前の入力の既定の形式に合わせてあるので、出した値をそのまま別の関数に渡せます。
- hex: 16進(小文字)。
- HEX: 16進(大文字)。
- base64: パディングあり。
- base64url: パディングなし。
- base32: RFC 4648。パディングあり。
- url: RFC 3986の非予約文字(
A-Z a-z 0-9 - . _ ~)以外をすべて%XX(大文字)にする。 - bech32: [SSP 2.9.14] BIP-173のbech32(小文字)。hrp(
npubなど。1~83文字)を--hrp=で指定する。--hrp=を受け付けるのはinternal_dataのConvertだけで、ほかの関数で--out=bech32にすると400(invalid parameter)。 - text:
--charset=の文字コード(既定はUTF-8)の文字列として返す。その文字コードとして正しく戻せないバイト列や、NUL・改行(SAORIのヘッダに書けない)を含むときは500(not text)。
応答は要求のCharsetの文字コードで返るので、そこで表せない文字は?に化けます。どんな文字でも受け取れるように、要求はCharset: UTF-8で送ってください。
--charset=には文字コードの名前(UTF-8、Shift_JIS、EUC-JPなど)を書きます。知らない名前やUTF-16は400(invalid parameter)です。
応答と失敗理由¶
成功すれば200でResultに結果が入ります。結果が空文字列のときは204です。真偽を返す関数は、200で1(真)または0(偽)を返します。「一致しない」などは失敗ではありません。
失敗時のErrorDescriptionには、共通のもののほかに次のものがあります。
- unsupported algorithm: 400。知らないアルゴリズム名。
- invalid encoding: 400。
--名前-in=の形式として読めない。圧縮データが壊れている。 - invalid key: 400。鍵の長さがアルゴリズムに合わない。公開鍵暗号の鍵として読めない、鍵の種類が方式に合わない。
- out of range: 400。数値・日時が扱える範囲の外。
- too large: 400。データが大きすぎる。
- file not found: 400。
--data-in=fileのファイルが無い。 - not text: 500。
--out=textで文字列に戻せない。 - file read failed: 500。ファイルを途中で読めなくなった。
- authentication failed: 500。復号できない(暗号文の改ざん・鍵の違いなど)。JWTを検証できない。
- expired / not yet valid: 500。JWTの有効期限(
exp)が切れている / 有効になる時刻(nbf)より前。 - internal error: 500。ベースウェア内部の想定外の失敗。
internal_data¶
データの形式の変換と、圧縮・展開。
Convert¶
データを別の形式で書き直す(base64⇔16進⇔パーセントエンコード⇔文字列など)。入力は--data-in=、出力は--out=で指定する。
- Argument1: data(既定の形式は
text) - オプション:
--data-in=/--out=(既定はtext) /--charset=/--hrp=([SSP 2.9.14]--out=bech32のhrp)
記述例¶
Argument0: Convert
Argument1: 48656c6c6f
Argument2: --data-in=hex
Argument3: --out=base64
→ Result: SGVsbG8=
Argument0: Convert
Argument1: 7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e
Argument2: --data-in=hex
Argument3: --out=bech32
Argument4: --hrp=npub
→ Result: npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg
対応: SSP 2.9.13
Compress¶
データを圧縮する。
- Argument1: 方式:
gzip/zlib/deflate(ヘッダの無い生のdeflate) - Argument2: data(既定の形式は
text) - オプション:
--data-in=/--level=(0~9。省略時はzlibの既定) /--out=(既定はbase64) /--charset=
対応: SSP 2.9.13
Decompress¶
圧縮されたデータを展開する。gzipは複数のメンバーをつないだものも読む。
壊れている・途中で切れている・後ろに余計なデータがあるときは400(invalid encoding)。展開した後の大きさも8MBまで(超えると400 too large)。
- Argument1: 方式:
gzip/zlib/deflate/brotli/zstd(brotliとzstdは展開だけ) - Argument2: data(既定の形式は
base64) - オプション:
--data-in=/--out=(既定はtext) /--charset=
記述例¶
Argument0: Decompress
Argument1: gzip
Argument2: H4sIAAAAAAAACvNIzcnJBwCCidH3BQAAAA==
→ Result: Hello
対応: SSP 2.9.13
internal_crypto¶
ハッシュ・MAC・共通鍵暗号・鍵の導出・パスワードのハッシュ・データの保護(DPAPI)・ワンタイムパスワード(TOTP)・公開鍵暗号の署名と鍵共有。
Digest / HMACのアルゴリズム名(DeriveKey / TOTPの--digest=も同じ)は次のとおりです。ハイフン・アンダースコアの有無と大文字小文字は区別しません(SHA-256もsha_256もsha256と同じ)。これ以外は400(unsupported algorithm)です。
md5 sha1 sha224 sha256 sha384 sha512 sha512-256 sha3-224 sha3-256 sha3-384 sha3-512 blake2b512 blake2s256
Digest¶
ハッシュ値を求める。
- Argument1: アルゴリズム名
- Argument2: data(既定の形式は
text。fileも使える) - オプション:
--data-in=/--out=(既定はhex) /--charset=
記述例¶
Argument0: Digest
Argument1: sha256
Argument2: abc
→ Result: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
文字列は既定でUTF-8のバイト列としてハッシュします。Shift_JISのバイト列のハッシュがほしいときは--charset=Shift_JISを付けてください。
対応: SSP 2.9.13
HMAC¶
HMACを求める。
- Argument1: アルゴリズム名
- Argument2: key(既定の形式は
text) - Argument3: data(既定の形式は
text。fileも使える) - オプション:
--key-in=/--data-in=/--out=(既定はhex) /--charset=
対応: SSP 2.9.13
Compare¶
2つのバイト列を、内容によらず同じ時間で比べる。等しければ1、違えば0(長さが違えば0)。
Webhookの署名の照合など、比べる時間から中身を推測されたくない場面に使う。16進の署名どうしを比べるときは--a-in=hex --b-in=hexにすると、大文字小文字の違いを気にしなくてよい。
- Argument1: a(既定の形式は
text) - Argument2: b(既定の形式は
text) - オプション:
--a-in=/--b-in=/--charset=
対応: SSP 2.9.13
共通鍵暗号(Encrypt / Decrypt)¶
アルゴリズムは次のとおりです(名前のハイフン・アンダースコアの有無と大文字小文字は区別しない)。これ以外は400(unsupported algorithm)です。
- aes-256-gcm(推奨) / aes-128-gcm / chacha20-poly1305: 認証付き暗号。改ざんされた暗号文は復号できない。暗号文の形は「nonce(12バイト)+暗号文+認証タグ(16バイト)」。
- aes-256-cbc / aes-128-cbc: PKCS#7のパディング。暗号文の形は「IV(16バイト)+暗号文」。改ざんを検出できないので、ほかのシステムとやりとりするときだけ使うこと。
- aes-256-ctr: 暗号文の形は「IV(16バイト)+暗号文」。改ざんを検出できない。
- chacha20: [SSP 2.9.14] 認証の付かないChaCha20(RFC 8439。nonceは12バイト、ブロックのカウンタは0から)。暗号文の形は「nonce(12バイト)+暗号文」。改ざんを検出できないので、ほかのシステム(nostrのNIP-44など、MACを別に付ける方式)とやりとりするときだけ使うこと。
鍵(key)の長さは、アルゴリズムの鍵長ちょうど(AES-256とChaCha20は32バイト、AES-128は16バイト)でなければ400(invalid key)です。パスワードから鍵を作るときはDeriveKeyを通してください。
nonce / IVは、暗号化のたびに乱数で作って暗号文の先頭に付けます。--iv=(nonce / IVを指定する)と--pack=none(先頭に付けない)は、ほかのシステムがnonceを別に受け渡す場合のためだけのものです。同じ鍵で同じnonce / IVを2回使うと、GCM・ChaCha20-Poly1305・CTRは暗号として破れます(平文が漏れ、改ざんもできるようになる)。--iv=に固定の値を書いて使い回さないでください。
Encrypt¶
共通鍵暗号で暗号化する。
- Argument1: アルゴリズム名(共通鍵暗号を参照)
- Argument2: key(既定の形式は
text) - Argument3: data(既定の形式は
text) - オプション:
--key-in=/--data-in=/--aad=(追加認証データ。認証付き暗号だけ。--aad-in=、既定の形式はtext) /--iv=(--iv-in=、既定の形式はhex) /--pack=none/--out=(既定はbase64) /--charset=
記述例¶
Argument0: Encrypt
Argument1: aes-256-gcm
Argument2: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
Argument3: secret message
Argument4: --key-in=hex
→ Result: (毎回違うbase64)
対応: SSP 2.9.13
Decrypt¶
Encryptの暗号文を復号する。
復号できなければ500(authentication failed)。認証付き暗号では、暗号文・認証タグ・--aad=のどれかが違えばこれになる。CBCではパディングが合わないとき。CTRは間違った鍵でも何かしらの値を返す。
- Argument1: アルゴリズム名
- Argument2: key(既定の形式は
text) - Argument3: ciphertext(既定の形式は
base64) - オプション:
--key-in=/--ciphertext-in=/--aad=/--aad-in=/--iv=/--iv-in=/--pack=none/--out=(既定はtext) /--charset=
--pack=noneのときは--iv=が要ります。--pack=noneでないとき(nonce / IVが暗号文の先頭にあるとき)に--iv=を付けると400(invalid parameter)です。
対応: SSP 2.9.13
DeriveKey¶
パスワードなどから、暗号の鍵などに使うバイト列を導出する。方式ごとのパラメータはオプションで指定し、その方式で使わないオプションを書くと400(invalid parameter)。
時間とメモリを使う処理なので、--asyncを推奨。
- Argument1: 方式:
pbkdf2/hkdf/hkdf-extract/hkdf-expand/scrypt/argon2id - Argument2: password(既定の形式は
text) - Argument3: salt(既定の形式は
text) - Argument4: 長さ(バイト。1~1024)
- オプション:
--password-in=/--salt-in=/ 下の方式ごとのオプション /--out=(既定はhex) /--charset=
- pbkdf2:
--digest=(既定はsha256) /--iterations=(既定は600000。1~10000000) - hkdf:
--digest=(既定はsha256) /--info=(--info-in=、既定の形式はtext)。passwordが入力の鍵(IKM)で、空にはできない。saltは空でもよい。 - hkdf-extract: [SSP 2.9.14] HKDFのextractだけ(RFC 5869 §2.2)。
--digest=(既定はsha256)。passwordが入力の鍵(IKM。空にはできない)、saltはそのまま。結果はPRKで、長さはハッシュの長さちょうど(sha256なら32)でなければ400(out of range)。 - hkdf-expand: [SSP 2.9.14] HKDFのexpandだけ(RFC 5869 §2.3)。
--digest=(既定はsha256) /--info=。passwordがPRK(空にはできない)。saltは空にする(空でなければ400invalid parameter)。長さはハッシュの長さ×255まで。 - scrypt:
--n=(既定は8192。2の累乗) /--r=(既定は8) /--p=(既定は10。1~16)。N×r×128バイトが12MiBまで。 - argon2id:
--iterations=(既定は3。1~1000) /--memory=(KiB。既定は12288。lanes×8~12288) /--lanes=(既定は1。1~16)。saltは8バイト以上、長さは4以上。バージョンは1.3(0x13)。秘密鍵(secret)と関連データ(associated data)は使えない。
範囲の外は400(out of range)です。既定のパラメータは、OWASPの推奨のうちメモリの上限(12MiB)に収まる組み合わせです。
記述例¶
Argument0: DeriveKey
Argument1: pbkdf2
Argument2: password
Argument3: salt
Argument4: 20
Argument5: --digest=sha1
Argument6: --iterations=4096
→ Result: 4b007901b765489abead49d926f721d065a429c1
対応: SSP 2.9.13
HashPassword¶
パスワードを保存用にハッシュし、PHC文字列(方式・パラメータ・ソルト・ハッシュをまとめた1行の文字列)で返す。ソルトは毎回16バイトの乱数、ハッシュは32バイト、パラメータは下の表で固定(OWASPの推奨のうち、メモリの上限12MiBに収まるもの)。
保存した文字列はVerifyPasswordで照合する。--asyncを推奨。
- Argument1: password(既定の形式は
text) - オプション:
--method=(下の表。既定はargon2id) /--password-in=/--charset=
- argon2id:
$argon2id$v=19$m=12288,t=3,p=1$ソルト$ハッシュ - scrypt:
$scrypt$ln=13,r=8,p=10$ソルト$ハッシュ(ln は N の2を底とする対数) - pbkdf2-sha256:
$pbkdf2-sha256$i=600000,l=32$ソルト$ハッシュ - bcrypt: [SSP 2.9.23]
$2b$12$+ソルト22文字+ハッシュ31文字。パスワードは先頭の72バイトまでしか使われない。 - sha512-crypt: [SSP 2.9.23]
$6$rounds=656000$ソルト$ハッシュ(ソルトは16文字) - sha256-crypt: [SSP 2.9.23]
$5$rounds=535000$ソルト$ハッシュ(ソルトは16文字) - md5-crypt: [SSP 2.9.23]
$1$ソルト$ハッシュ(ソルトは8文字) - apr1: [SSP 2.9.23]
$apr1$ソルト$ハッシュ(ソルトは8文字。Apacheのhtpasswdの形式) - bsdi-crypt: [SSP 2.9.23]
_+反復回数4文字+ソルト4文字+ハッシュ11文字(BSDiの拡張DES。反復回数は5001) - des-crypt: [SSP 2.9.23] ソルト2文字+ハッシュ11文字(伝統的なcrypt(3)のDES)。パスワードは先頭の8文字までしか使われない。
ソルトとハッシュはbase64(パディングなし)です。
[SSP 2.9.23] bcrypt以下はcrypt(3)の形式で、ほかのシステム(/etc/shadow、htpasswd、PHPのpassword_hashなど)とやりとりするためのもの。--method=の名前のハイフン・アンダースコアと大文字小文字は無視する。ソルトとハッシュは各形式の文字(./0-9A-Za-z)で、パラメータは上の表で固定。md5-crypt・apr1・bsdi-crypt・des-cryptは今では弱い方式なので、相互運用のほかには使わないこと。crypt(3)の形式ではパスワードにNUL(0x00)を含められず、含めると400(invalid parameter)。1024バイトを超えるパスワードは400(out of range)。
対応: SSP 2.9.13
VerifyPassword¶
パスワードがPHC文字列と一致するかを調べ、一致すれば1、しなければ0を返す。パラメータはPHC文字列から読む。--asyncを推奨。
HashPasswordの出力のほか、同じ形のものなら他の実装が作った文字列も読める($argon2id$(v=の無いもの・v=16も可) / $scrypt$ / $pbkdf2-sha1$ / $pbkdf2-sha256$ / $pbkdf2-sha512$)。ただしメモリの上限(12MiB)を超えるパラメータのもの(argon2-cffiの既定の64MiBなど)は400(out of range)で照合できない。
[SSP 2.9.23] crypt(3)の形式も読める($2a$ / $2b$ / $2y$(bcrypt。3つとも同じに扱う) / $6$ / $5$(rounds=の無いもの・1000未満を1000に切り上げるものも可) / $1$ / $apr1$ / _で始まる20文字(BSDiの拡張DES) / 13文字(伝統的なDES))。bcryptのコストは16まで、SHA-cryptのrounds=は10000000までで、それを超えると400(out of range)。$2x$・$2$は扱わない。
これ以外の方式は400(unsupported algorithm)、形の誤りは400(invalid parameter)、パラメータがDeriveKeyの範囲の外なら400(out of range)。
- Argument1: password(既定の形式は
text) - Argument2: PHC文字列([SSP 2.9.23] またはcrypt(3)の形式の文字列)
- オプション:
--password-in=/--charset=
対応: SSP 2.9.13
Protect¶
データを、OSのデータ保護機能(WindowsのDPAPI)で包む。包んだものは、同じPCの同じユーザー(--scope=machineなら同じPCの全ユーザー)でしかUnprotectできない。
セーブデータに置くトークンなどを、ほかのPCやほかのユーザーにコピーされても読めないようにするためのもので、ゴースト同士を分けるものではない(同じユーザーで動くほかのゴーストやプログラムは戻せる)。
- Argument1: data(既定の形式は
text) - オプション:
--data-in=/--scope=user(既定) /--scope=machine/--out=(既定はbase64) /--charset=
セーブデータを別のPCへ持っていった場合(SSPのセーブデータ共有、クラウドストレージでの同期、手でのコピーなど)や、Windowsのユーザーを作り直した場合は、Unprotectできなくなります。スクリプトはUnprotectの失敗を「ログインし直してもらう」などとして扱ってください。
対応: SSP 2.9.13
Unprotect¶
Protectで包んだデータを元に戻す。戻せなければ500(authentication failed)。
- Argument1: data(既定の形式は
base64) - オプション:
--data-in=/--out=(既定はtext) /--charset=
対応: SSP 2.9.13
TOTP¶
時刻ベースのワンタイムパスワード(RFC 6238。2段階認証のアプリが出す数字)を求める。桁数に満たなければ先頭を0で埋める。
- Argument1: key(既定の形式は
base32。認証アプリに登録する秘密鍵の文字列) - オプション:
--key-in=/--digits=(既定は6。6~10) /--period=(秒。既定は30。1~86400) /--digest=(既定はsha1。md5は使えない) /--time=(日時。internal_timeの日時の引数と同じ書き方で、ゾーンの無いものはOSのタイムゾーンの時刻。既定は現在時刻) /--charset=
記述例¶
Argument0: TOTP
Argument1: GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
Argument2: --time=59
Argument3: --digits=8
→ Result: 94287082
対応: SSP 2.9.13
公開鍵暗号(GenerateKeyPair / Sign / Verify / KeyAgreement)¶
署名の方式は次のとおりです(名前のハイフン・アンダースコアの有無と大文字小文字は区別しない)。これ以外は400(unsupported algorithm)です。ed25519~ps256は、それぞれJWT(JWS)のalgのEdDSA / ES256 / RS256 / PS256と同じ署名になります。
- ed25519: Ed25519(RFC 8032)。署名は64バイト。
- es256: ECDSA(P-256)+ SHA-256。署名は64バイト(r‖s。JWSと同じ形で、DER形式ではない)。
- rs256: RSA(RSASSA-PKCS1-v1_5)+ SHA-256。
- ps256: RSA(RSASSA-PSS)+ SHA-256。MGF1もSHA-256、ソルトは32バイト(JWAのPS256と同じ)。
- bip340: secp256k1のSchnorr署名(BIP-340。nostrのイベントの署名と同じ)。署名は64バイト。メッセージはハッシュせずにそのまま署名する(長さは自由。nostrではイベントのID(32バイト)を
--data-in=hexで渡す)。鍵の渡し方はほかの方式と違う(下を参照)。
鍵は、1行のDERのbase64か、PEMで渡します。GenerateKeyPairは前者(秘密鍵はPKCS#8、公開鍵はSubjectPublicKeyInfo)を出力します。
PEMは改行を含みSAORIのヘッダに書けないので、改行を空白にしたもの(または改行を除いて1行にしたもの)を渡してください。-----BEGINで始まればPEMとして、-----BEGIN ...-----と-----ENDの間をbase64として読みます。
秘密鍵はPKCS#8(PRIVATE KEY)のほか、RSAはPKCS#1(RSA PRIVATE KEY)、ECDSAはSEC1(EC PRIVATE KEY)も読めます。パスワードで暗号化された秘密鍵は読めません。公開鍵はSubjectPublicKeyInfo(PUBLIC KEY)です。
RSAの鍵は2048~4096ビット、ECDSAの鍵はP-256だけです。読めない鍵・種類や大きさが方式に合わない鍵は400(invalid key)です。
bip340とKeyAgreementのsecp256k1の鍵は、DER / PEMではなく生の値です。秘密鍵は32バイト、公開鍵はx座標だけの32バイト(BIP-340の形。nostrの公開鍵と同じ)で、既定の形式はhexです。--key-in=で読み方を変えられます(--key-in=bech32ならnsec1... / npub1...をそのまま渡せる)。
長さが違う鍵、0や位数以上の秘密鍵は400(invalid key)です。--key-in=はこの2つの方式でだけ使えます(ほかの方式で付けると400 invalid parameter)。
GenerateKeyPair¶
公開鍵暗号の鍵ペアを作る。Value0に秘密鍵(PKCS#8)、Value1に公開鍵(SubjectPublicKeyInfo)が、どちらもDERのbase64で入る。ResultはValue0と同じ。
RSAの鍵の生成は数秒かかることがあるので、--asyncを推奨(非同期の呼び出しでは、秘密鍵がReference4、公開鍵がReference5に入る)。
秘密鍵は、必要ならProtectで包んで保存してください。
- Argument1: 方式:
ed25519/es256(P-256) /rsa-2048/rsa-3072/rsa-4096(RSAの鍵はrs256とps256のどちらにも使える) /bip340(secp256k1)
bip340だけは、Value0に秘密鍵(32バイト)、Value1にx座標だけの公開鍵(32バイト)が、どちらも16進(小文字)で入る。nostrのnsec / npubにするときはConvertの--out=bech32を使う。この鍵はKeyAgreementのsecp256k1にもそのまま使える。
対応: SSP 2.9.14
Sign¶
秘密鍵でデータに署名する。
- Argument1: 方式(公開鍵暗号を参照)
- Argument2: 秘密鍵
- Argument3: data(既定の形式は
text) - オプション:
--data-in=/--out=(既定はbase64url。bip340はhex) /--charset=/--key-in=(bip340だけ。既定はhex) /--aux=(bip340だけ。BIP-340の補助の乱数32バイト。--aux-in=、既定の形式はhex。省略時は毎回乱数。テストベクタと突き合わせるとき以外は使わないこと)
記述例¶
Argument0: Sign
Argument1: ed25519
Argument2: MC4CAQAwBQYDK2VwBCIEIJ1hsZ3v/VpguoRK9JLsLMREScVpezJpGXA7rAMcrn9g
Argument3: eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc
→ Result: hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg
Argument0: Sign
Argument1: bip340
Argument2: b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfef
Argument3: 243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89
Argument4: --data-in=hex
Argument5: --aux=0000000000000000000000000000000000000000000000000000000000000001
→ Result: 6896bd60eeae296db48a229ff71dfe071bde413e6d43f917dc8dcf8c78de33418906d11ac976abccb20b091292bff4ea897efcb639ea871cfa95f6de339e4b0a
(BIP-340のテストベクタ1)
Ed25519とRSA(rs256)は、同じ鍵とデータなら毎回同じ署名になります。es256・ps256・bip340は毎回違う署名になります(どれもVerifyで検証できる)。
対応: SSP 2.9.14
Verify¶
署名を公開鍵で検証する。正しければ1、正しくなければ0(署名の長さが違うときも0)。公開鍵の代わりに秘密鍵を渡してもよい(bip340を除く)。
bip340では、32バイトだが曲線上の点にならない公開鍵も、BIP-340の決まりどおり0になる(長さが違えば400 invalid key)。
- Argument1: 方式(公開鍵暗号を参照)
- Argument2: 公開鍵
- Argument3: data(既定の形式は
text) - Argument4: signature(既定の形式は
base64url。bip340はhex) - オプション:
--data-in=/--signature-in=/--charset=/--key-in=(bip340だけ。既定はhex)
対応: SSP 2.9.14
KeyAgreement¶
自分の秘密鍵と相手の公開鍵から、2人だけが同じ値を得られる共有の秘密を求める(ECDH)。結果は点のx座標(32バイト)そのままで、ハッシュはしない(nostrのNIP-44と同じ)。
結果をそのまま暗号の鍵にしないこと。DeriveKeyのhkdf(またはhkdf-extract)を通してから使ってください。
- Argument1: 方式:
secp256k1 - Argument2: 秘密鍵(32バイト。既定の形式は
hex) - Argument3: 相手の公開鍵(既定の形式は
hex)。x座標だけの32バイト(BIP-340 / nostrの形)、圧縮形式の33バイト、非圧縮形式の65バイトのどれでもよい - オプション:
--key-in=/--peer-in=/--out=(既定はhex)
秘密鍵が範囲外、相手の公開鍵が曲線上の点でない・長さが違うときは400(invalid key)です。
記述例¶
Argument0: KeyAgreement
Argument1: secp256k1
Argument2: 315e59ff51cb9209768cf7da80791ddcaae56ac9775eb25b6dee1234bc5d2268
Argument3: c2f9d9948dc8c7c38321e4b85c8558872eafa0641cd269db76848a6073e69133
→ Result: 5ad01c0fc57ed5f92a0518b6440ca876eb1f247ddbb237ba1caaabbf4cebbdce
Argument0: DeriveKey
Argument1: hkdf-extract
Argument2: 5ad01c0fc57ed5f92a0518b6440ca876eb1f247ddbb237ba1caaabbf4cebbdce
Argument3: nip44-v2
Argument4: 32
Argument5: --password-in=hex
→ Result: 3dfef0ce2a4d80a25e7a328accf73448ef67096f65f79588e358d9a0eb9013f1
(NIP-44の会話鍵。NIP-44のテストベクタ)
対応: SSP 2.9.14
internal_random¶
暗号論的に安全な乱数(OpenSSLのRAND_bytes)。
Bytes¶
ランダムなバイト列。ソルトやトークンの生成などに。
- Argument1: バイト数(1~1024。範囲外は400
out of range) - オプション:
--out=(既定はhex)
対応: SSP 2.9.13
Number¶
最小以上・最大以下の整数を、偏りなく1つ選ぶ。64ビット符号付き整数の範囲で使える。最小が最大より大きければ400(invalid parameter)。
- Argument1: 最小
- Argument2: 最大
対応: SSP 2.9.13
UUID¶
UUID(RFC 9562)を小文字の正規の表記(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)で返す。
バージョン4は全体がランダム。バージョン7は先頭の48ビットが現在時刻(UNIX時刻のミリ秒)で、作った順に並べやすい(同じミリ秒の中での順序は保証しない)。
- オプション:
--version=4(既定) /--version=7
対応: SSP 2.9.13
internal_time¶
日時の整形と計算。日時は内部ではUTCのエポック秒(1970-01-01T00:00:00Zからの秒数。YAYAのGETSECCOUNTと同じ単位)で扱います。
日時の引数¶
日時を受け取る引数には、次のどれかを書きます。どれとしても読めなければ400(invalid parameter)です。存在しない日付(2月30日など)も400です。
- 整数: エポック秒。負の値(1970年より前)も書ける。
- now: 現在時刻。
- ISO 8601:
2026-10-11、2026-10-11T21:00:00+09:00、2026-10-11 21:00、20261011T210000Zなど。小数秒は読み捨てる。
ゾーン(Zや+09:00)の無いものは、--tz=のゾーンの時刻として読む。 - HTTPの日付:
Sun, 11 Oct 2026 12:00:00 GMTなど。
扱える範囲はUTCで0001-01-01~9999-12-31です(--tz=localのときは1601年以降)。範囲の外は400(out of range)です。
ただし、ISO 8601・HTTPの日付の文字列で書けるのは1601年以降です(それより前は400 invalid parameter。整数のエポック秒なら書けます)。
範囲はUTCで決まるので、9999-12-31の近くを東(+09:00など)のゾーンで整形すると、年が10000(5桁)になることがあります。
ゾーン(--tz=)¶
- local: (既定)OSのタイムゾーン。その日時の夏時間を反映する。
- UTC / Z: UTC。
- +09:00 / +0900 / +09: UTCからのオフセット(±18:00まで)。
- 名前付きのゾーン: IANAのタイムゾーン名(
Asia/Tokyo、America/New_Yorkなど)か、Windowsのタイムゾーン名(Tokyo Standard Timeなど)。大文字小文字は区別しない。その日時の夏時間を反映する。
ゾーンの規則はOS(Windows)のタイムゾーン情報を使う。IANAの名前は、Unicode CLDRの対応表でWindowsの名前に引き直す。OSが持っていない名前は400(invalid parameter)。UTCとして扱うことはしない。
名前付きのゾーンの規則はOSのタイムゾーン情報の範囲のものです。OSの情報に無い昔の規則(多くは2000年代より前)は、OSが持つ最初の年の規則で代用されます。
Format¶
日時を--tz=のゾーンの日時として整形する。曜日・月名は英語で固定(言語・地域の設定によらない)。
- Argument1: 日時
- Argument2: 書式(省略時・空のときは
iso8601) - オプション:
--tz=
書式は次の名前か、%を使った書式です。知らない書式名・知らない%の文字は400(invalid parameter)です。
- iso8601 / rfc3339:
2026-10-11T21:00:00+09:00。オフセットが0なら末尾はZ。 - http:
Sun, 11 Oct 2026 12:00:00 GMT。--tz=によらず常にGMT。 - epoch / epochms: エポック秒 / エポックミリ秒。
- %書式:
%Y(西暦4桁)%y(西暦の下2桁)%m(月)%d(日)%H(時)%M(分)%S(秒)%j(年の何日目)%u(曜日 月=1~日=7)%w(曜日 日=0~土=6)%a(Sun)%A(Sunday)%b(Jan)%B(January)%z(+0900)%:z(+09:00)%s(エポック秒)%%(%)。
%以外の文字はそのまま出力する。
記述例¶
Argument0: Format
Argument1: 0
Argument2: %Y/%m/%d %H:%M (%a)
Argument3: --tz=+09:00
→ Result: 1970/01/01 09:00 (Thu)
対応: SSP 2.9.13
Add¶
日時に量を足した日時を、エポック秒で返す。量は負でもよい。
second / minute / hourは経過時間で足す。day以上は--tz=のゾーンの暦で足す(夏時間の切り替えをまたいでも同じ時刻になる)。
月末は丸める(1月31日+1か月=2月28日または29日)。
- Argument1: 日時
- Argument2: 量(整数)
- Argument3: 単位:
secondminutehourdayweekmonthyear(複数形のdaysなども可) - オプション:
--tz=
対応: SSP 2.9.13
Diff¶
日時2-日時1を、単位で数えた整数で返す。端数は0の方向へ切り捨てる。
day以上は--tz=のゾーンの暦で数える。たとえば日時2が日付だけ(その日の0時)なら、「あと何日」は--tz=のゾーンで日付が変わる時刻に1つ減る。month / yearは暦の月で数え、Addで足して越えない最大の数を返す(1月31日からは、平年なら2月28日、うるう年なら2月29日の同じ時刻で1か月)。
- Argument1: 日時1
- Argument2: 日時2
- Argument3: 単位(Addと同じ)
- オプション:
--tz=
記述例¶
Argument0: Diff
Argument1: 2026-12-20T10:00
Argument2: 2026-12-24
Argument3: day
→ Result: 3(3日と14時間なので3)
カレンダーの日付どうしの差(「今日から何日後か」)がほしいときは、日時1も日付だけにしてください(Formatの%Y-%m-%dで今日の日付を作れます)。
対応: SSP 2.9.13
internal_text¶
文字列の処理。
Normalize¶
文字列をUnicodeの正規化形式にそろえる。半角カナと全角カナ、合成済みの文字と結合文字の並びなど、見た目が同じで符号が違う文字列を比べる前にそろえるのに使う。
OSの機能(Windows Vista以降のNormalizeString)を使う。使えないOSでは500(internal error)。対になっていないサロゲートを含む文字列は400(invalid encoding)。
- Argument1: 形式:
NFC/NFD/NFKC/NFKD(大文字小文字は区別しない。これ以外は400unsupported algorithm) - Argument2: 文字列
記述例¶
Argument0: Normalize
Argument1: NFKC
Argument2: ガイド①
→ Result: ガイド1
対応: SSP 2.9.13
internal_jwt¶
JWT(RFC 7519)の作成と検証。形式はJWSのコンパクト形式(ヘッダ.ペイロード.署名)だけです。
algは次のとおりです(引数・--alg=では大文字小文字を区別しない)。noneを含め、これ以外は400(unsupported algorithm)です。
- HS256 / HS384 / HS512: HMAC(共通鍵)。鍵は
--key-in=の形式(既定はtext。文字列はUTF-8)で渡す。鍵はハッシュの長さ以上(HS256は32バイト、HS384は48バイト、HS512は64バイト)でなければ400(invalid key。RFC 7518の要求)。 - RS256 / PS256 / ES256 / EdDSA: 公開鍵暗号。鍵の渡し方と方式の詳細はinternal_cryptoの公開鍵暗号と同じ(
EdDSAはEd25519だけ)。--key-in=は使えない(400invalid parameter)。
Sign¶
ペイロードに署名して、JWTを作る。
ペイロードはJSONのオブジェクトで、書かれた文字列のまま(UTF-8で)使う。JSONのオブジェクト1つでなければ400(invalid parameter)。
ヘッダは{"alg":"HS256","typ":"JWT"}の形。--header=にJSONのオブジェクトを書くと、その項目(kidなど)を足せる(typを書けばそれが優先。algを書くときはArgument1と同じ値でなければ400 invalid parameter)。
- Argument1: alg
- Argument2: 鍵(HS*は共通鍵、ほかは秘密鍵)
- Argument3: ペイロード(JSON)
- オプション:
--header=(JSON) /--key-in=(HS*だけ)
記述例¶
Argument0: Sign
Argument1: HS256
Argument2: 0123456789abcdef0123456789abcdef
Argument3: {"sub":"sakura","exp":4102444800}
→ Result: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzYWt1cmEiLCJleHAiOjQxMDI0NDQ4MDB9.eJKcQzLKm4FjTltXWvPqonVpfwhVaBgpvf9nZw0szuM
対応: SSP 2.9.14
Verify¶
JWTの署名と有効期限を検証して、正しければペイロード(JSON)を返す。ペイロードに改行があれば空白に置き換えて返す(JSONとしての意味は変わらない)。
algは--alg=で必ず指定する(無ければ400 invalid parameter)。トークンのヘッダのalgは信用せず、指定と一致しなければ失敗にする(algのすり替えやnoneによる偽造を防ぐため)。
exp(有効期限)とnbf(有効になる時刻)があれば確かめる。iss・aud・iatなどほかの項目は見ないので、必要ならスクリプトで確かめること。
- Argument1: トークン
- Argument2: 鍵(HS*は共通鍵、ほかは公開鍵)
- オプション:
--alg=(必須) /--leeway=(秒。時計のずれの許容。既定は0。0~86400) /--key-in=(HS*だけ) /--time=(現在時刻の代わりに使う日時。internal_timeの日時の引数と同じ書き方。既定は現在時刻)
失敗は次のとおりです。
- 500 authentication failed: 署名が合わない。ヘッダの
algが--alg=と違う(大文字小文字も区別する)。ヘッダにcritがある。トークンの形が壊れている(3つに分かれない、base64urlとして読めない、ヘッダ・ペイロードがJSONのオブジェクトでない、UTF-8として正しくない)。exp/nbfが数値でない。 - 500 expired: 現在時刻が
exp+leeway以降(expの時刻ちょうどで失効)。 - 500 not yet valid: 現在時刻+leewayが
nbfより前。 - 400 invalid key: 鍵が読めない、種類が合わない、HS*の鍵が短い。
記述例¶
Argument0: Verify
Argument1: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzYWt1cmEiLCJleHAiOjQxMDI0NDQ4MDB9.eJKcQzLKm4FjTltXWvPqonVpfwhVaBgpvf9nZw0szuM
Argument2: 0123456789abcdef0123456789abcdef
Argument3: --alg=HS256
→ Result: {"sub":"sakura","exp":4102444800}
対応: SSP 2.9.14
internal_file¶
ファイルそのものを対象にする処理。(ファイルの中身をバイト列として読むだけの処理は、internal_cryptoのDigest / HMACの--data-in=fileのように、それぞれの関数が受け付けます。)
MediaInfo¶
音声・動画・画像ファイルのタグ・長さ・大きさを、ファイルをロードせずに(再生の準備をせずに、ヘッダやタグだけを読んで)取得する。\![sound]で要素を作らなくても、パスだけで読める。sound.*の一覧に要素は増えず、OnSound*イベントも出ず、一時ファイルも作らない。
環境によっては取得できない場合がある。SSPはOSのプロパティハンドラ(エクスプローラーのファイルの詳細に出るのと同じ仕組み)に頼って読むので、OSにその形式のハンドラが無い、あるいはハンドラが一部の項目を返さない形式では、その値は空になる(タグが無いファイルと区別はできない)。取れなかったことはエラーにならない。
同期で呼ぶと、読み出しの間ゴーストが止まる点に注意。読み出しはファイルの置き場所(ネットワークドライブなど)によっては時間がかかるので、--asyncの利用も検討する。
- Argument1: ファイルのパス。相対パスはゴーストのフォルダ(ghost/master)が基準。区切りは
/も\も書ける。ゴーストなしの呼び出しで相対パスを渡すと400(ghost required)。ファイルが無い(フォルダを含む)ときは400(file not found)。URLは受け付けない。 - Argument2以降: 欲しい値のキー。1個以上必須(1個もなければ400
invalid parameter)で、11個まで。キーは大文字小文字を区別しない。
キーは次のとおり。値はすべて文字列(数値は10進の整数)。タグの項目は、sound.(要素名).meta.*と同じ名前と意味。
- title / artist / albumartist / album / genre / comment: 文字列。
- year: 4桁の西暦。元の値が日付全体なら先頭の4桁。
- track: 曲番号の整数だけ。総数は含めない(
3/12なら3)。 - duration: 音声・動画の長さ(ミリ秒)。画像は空。VBRのMP3などは推定値のことがある。
- width / height: 画素数(px)。動画は映像のサイズ、画像は画像のサイズ。音声は空。アルバムアートなど、埋め込まれたジャケット画像だけの映像は動画と数えない。
応答のResultは種別で、audio(音声)/ video(動画)/ image(画像)のどれか。判別できなければ空。種別は目安なので、Resultが空でもValueは埋まることがある。Resultで分岐せず、Valueが空かどうかで判断すること。今後、種別が増えることがある。
Value0以降は、Argument2以降に並べたキーと同じ順に対応する。取れなかった値・知らないキーは空文字(エラーにはならない。実装独自のキーは返さない)。読めない・未対応の形式でも、400にはならず、ResultもValueもすべて空の200で返る。
- 値の中の改行は、バイト値
0x01に置き換える(CRLFも0x011つ。LFだけ・CRだけもそれぞれ0x011つ)。NULは除く。 - 複数の値を持つタグ(複数のアーティストなど)は、
;(セミコロンと半角空白)でつないだ1つの文字列にする。 - 応答の文字コードは要求の
Charsetに従う。 - ファイルに書かれたタグの文字コードの推定(ID3v1や、実際は別の文字コードなのに誤った文字コードとして書かれたタグなど)はOSまかせで、文字化けすることがある。
- どの形式のどの項目が取れるかはOSの環境で異なる。SSPで確かめた範囲では、Windows 11の標準の環境で、WAVのタグ、MP3のコメント、MKV / WebMのほとんどのタグと映像サイズなどは取れなかった。
記述例¶
Argument0: MediaInfo
Argument1: sound/bgm01.mp3
Argument2: title
Argument3: artist
Argument4: duration
Argument5: --async=OnMediaInfo
→ 204(結果はOnMediaInfoイベントで届く)
Reference0: OnMediaInfo
Reference1: audio
Reference2: internal_file
Reference3: MediaInfo
Reference4: タイトル
Reference5: アーティスト
Reference6: 215000
対応: SSP 2.9.25
CompressArchive¶
ファイルまたはフォルダをZIPファイルに圧縮する。SSTPのEXECUTEのCompressArchiveと同じ処理を、SAORI over DirectSSTPから呼べるようにしたもの。
書庫の処理は数秒かかることがあるので、--asyncを強く推奨する。同期で呼ぶと、処理の間ゴーストが止まる。
- Argument1: 圧縮先のZIPファイルのパス。相対パスはゴーストのフォルダ(ghost/master)が基準。区切りは
/も\も書ける。ゴーストなしの呼び出しで相対パスを渡すと400(ghost required)。 - Argument2: 圧縮するフォルダ(中のファイルを再帰的に含める)またはファイルのパス。書き方はArgument1と同じ。無ければ400(
file not found)。
オプション(同じ名前を2回書くと後のものが有効):
- --password=パスワード……指定したパスワードでAES暗号化ZIPにする。
- --option=オプション……次の値をカンマ区切りで1つにまとめて指定する(
--option=overwrite,lzmaのように。--option=を2つに分けて書くと後のものだけが有効になる)。
overwrite……圧縮先のZIPファイルが既にあれば置き換える。
append……圧縮先のZIPファイルが既にあれば追加する。
lzma……LZMAで圧縮する。
headerencrypt……ヘッダも暗号化する(--password=が必要)。
圧縮先のZIPファイルが既にあって、overwriteもappendも指定していないときは400(file exists)。
成功すると200で、Resultに圧縮したファイル数、Value0に圧縮後のバイト数、Value1に圧縮前(展開後)のバイト数が入る(10進の整数)。SSTPのEXECUTEのOnCompressArchiveCompleteのReference1~Reference3と同じ値で、--asyncではReference1(Result)とReference4・Reference5(Value0・Value1)に入る。
[SSP 2.9.25] ZIPでは、このファイル数とバイト数が正しい値にならないことがある(圧縮では0になる)。圧縮そのものの成否には影響しない。
失敗は次のとおり。400はfile not found(圧縮元が無い)/ invalid parameter(引数が足りない、パスが空、オプションが不正)/ ghost required / file exists / path not allowed(圧縮先がSSP管理下のフォルダの外)。500はopen failedなど、圧縮に失敗した理由。
セキュリティ上の観点から、圧縮先は、SSP管理下のフォルダ(ghostフォルダ、balloonフォルダなど「フォルダ」設定で指定した場所)以下に制限される。パスは..などを解決した結果で調べる。また、この制限は適宜強化される可能性があるため、ゴースト自身のデータが置かれているフォルダ以下に限定するのが無難。
記述例¶
Argument0: CompressArchive
Argument1: save/backup.zip
Argument2: save/data
Argument3: --option=overwrite
Argument4: --async=OnBackupDone
→ 204(結果はOnBackupDoneイベントで届く)
対応: SSP 2.9.25
ExtractArchive¶
ZIPファイルを展開する。SSTPのEXECUTEのExtractArchiveと同じ処理を、SAORI over DirectSSTPから呼べるようにしたもの。
書庫の処理は数秒かかることがあるので、--asyncを強く推奨する。同期で呼ぶと、処理の間ゴーストが止まる。
- Argument1: 展開するZIPファイルのパス。相対パスはゴーストのフォルダ(ghost/master)が基準。区切りは
/も\も書ける。ゴーストなしの呼び出しで相対パスを渡すと400(ghost required)。ファイルが無い(フォルダを含む)ときは400(file not found)。 - Argument2: 展開先のフォルダのパス。書き方はArgument1と同じ。フォルダが無ければ作る(作れなければ500
create directory failed)。
オプションは--asyncだけ。パスワードを指定するオプションは無い(パスワード付きのZIPは展開できないことがある)。
成功すると200で、Resultに展開したファイル数、Value0に圧縮後のバイト数、Value1に展開後のバイト数が入る(10進の整数)。SSTPのEXECUTEのOnExtractArchiveCompleteのReference1~Reference3と同じ値で、--asyncではReference1(Result)とReference4・Reference5(Value0・Value1)に入る。
[SSP 2.9.25] ZIPでは、このファイル数とバイト数が正しい値にならないことがある(複数のファイルを含むZIPでは、バイト数が最後の1ファイル分になる)。展開そのものの成否には影響しない。
失敗は次のとおり。400はfile not found(展開するZIPファイルが無い)/ invalid parameter(引数が足りない、パスが空)/ ghost required / path not allowed(展開先がSSP管理下のフォルダの外)。500はcreate directory failedか、展開に失敗した理由(open failed、crc、password requiredなど)。
セキュリティ上の観点から、展開先は、SSP管理下のフォルダ(ghostフォルダ、balloonフォルダなど「フォルダ」設定で指定した場所)以下に制限される。パスは..などを解決した結果で調べる。また、この制限は適宜強化される可能性があるため、ゴースト自身のデータが置かれているフォルダ以下に限定するのが無難。
記述例¶
Argument0: ExtractArchive
Argument1: save/backup.zip
Argument2: save/restore
Argument3: --async=OnRestoreDone
→ 204(結果はOnRestoreDoneイベントで届く)
Reference0: OnRestoreDone
Reference1: 12(展開したファイル数)
Reference2: internal_file
Reference3: ExtractArchive
Reference4: 圧縮後のバイト数
Reference5: 展開後のバイト数
対応: SSP 2.9.25
MediaInfoを実装するベースウェア向けの注記¶
MediaInfoは、他のOSのベースウェアでも既存の部品(ffprobe / libavformat、TagLib、OS標準のメディアAPIなど)で無理なく実装できる範囲に絞ってある。
- 必ず守ること(MUST):上のキーの名前・単位・形、取れなかった値は空文字にすること、値の整形(改行の
0x01、NULの除去)、相対パスの基準(ghost/master)、応答の形(Result+Value0..)、仕様にないキーは空にして独自のキーを返さないこと、パスの区切りに/を受け付けること。 - 望ましいこと(SHOULD):パスの区切りに
\も受け付けること(ゴーストの作者は\で書くが、他のOSでは\が普通のファイル名の文字になりうる)。複数の値のタグを;でつなぐこと。 - 実装によって違ってよいこと:対応する形式(ICO、HEICなどの追加の形式は、取れれば取れたでよく、取れなくても不具合ではない)、元のタグの文字コードの推定、VBRの長さの精度、
Resultの判定方法、どの形式のどの項目が取れるか。形式ごとのタグの対応(ID3v2のTIT2がtitleになる、など)は仕様では決めない。 - 適合の確かめ方:値が出たら正しいこと。出ないこと(空)は合格。
durationは誤差を許す(目安は±100ミリ秒か1%)。
ffprobe / libavformatで実装するときの目安:
durationはformat.duration。width/heightは、動画は映像ストリーム、画像は画像自体の値。- OggやOpusのタグは
format.tagsではなくstreams[].tagsにある。 - MP3やM4Aの埋め込みジャケット画像は、
attached_picつきの映像ストリームとして報告される。これを映像と数えないと、音声がvideoになってしまう。 track(3/12の形)とdate(2024-05-06の形)は、仕様のtrack(曲番号だけ)とyear(先頭の4桁)の形に整える必要がある。
internal_system¶
内蔵モジュールの一覧。どの関数が使えるかをスクリプトから確かめるのに使う。
一覧に出るのは名前がinternal_で始まるモジュールだけです(互換モジュールは出ません)。
結果はResultにカンマ区切りで、Value0以降に1つずつ入ります。並び順に意味はありません。
ListModules¶
内蔵モジュールの名前の一覧を返す(internal_system自身も含む)。
- 引数: なし
記述例¶
Argument0: ListModules
→ Result: internal_crypto,internal_random,internal_time,internal_data,internal_text,internal_jwt,internal_system,internal_file
→ Value0: internal_crypto
→ Value1: internal_random
…
対応: SSP 2.9.14
ListFunctions¶
指定した内蔵モジュールの関数名の一覧を返す。関数名は正規の綴り(大文字小文字)で返る。
内蔵モジュールでない名前(互換モジュールを含む)は400(unknown module)。
- Argument1: モジュール名(大文字小文字は区別しない)
記述例¶
Argument0: ListFunctions
Argument1: internal_jwt
→ Result: Sign,Verify
→ Value0: Sign
→ Value1: Verify
対応: SSP 2.9.14
互換モジュール¶
SSPは、よく使われていた次のSAORI DLLの互換の機能を内蔵しています。ゴーストがこれらのDLLをloadすると、SSPはDLLを読まずに内蔵の機能に差し替えます(ファイル名で判定します)。
[SSP 2.9.13] これらはDirect SSTPからも、DLL名から.dllを除いた名前(Module: stringなど)で呼べます。引数と戻り値は原作のSAORIのままです。
houchi clipcopy textcopy saori_ip saori_cpuid mciaudio cpuusage httpc string hanayu
- Direct SSTPには
load/unloadが無いので、ゴーストごとに状態を持つもの(mciaudio/httpc/string/hanayu)は、そのゴーストの最初の呼び出しで用意し、ゴーストの終了(SHIORIの再読み込みを含む)で捨てます。DLLとしてloadしたものとは状態を共有しません。 - ゴーストなしの呼び出しでは、ゴーストに依存する処理(
httpcの取得・保存とbg、hanayuのshow、mciaudioのload/play/loop/stop)は400(ghost required)になります。 houchiはloadしたときに働くものなので、Direct SSTPから呼んでも何も起きません。- Direct SSTPの要求はSSPの主スレッドで処理されるので、
httpcの取得はbgを付けて行ってください(付けないと、取得の間SSPの画面が止まります)。