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で始まる返事でなければ使えない」「返事が来なければ使えない」と判定してください。

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)です。
時間のかかる処理(大きなファイルのハッシュ、パスワードのハッシュ、鍵の導出など)には--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を使います。

  1. FMOなどから、自分のゴーストの窓(hwnd)を得る。
  2. 返事を受ける窓を用意する。
  3. SAORI/1.0の要求を組み立てて、ModuleとHWnd(返事を受ける窓)を付ける。
  4. ゴーストの窓へWM_COPYDATA(dwData = 9801)をSendMessage系の関数で送る。
  5. 返事を受ける窓にWM_COPYDATA(dwData = 9801)で届いた、SAORI/で始まるデータを応答として読む。

使えるかどうかは、最初にGET Versionで確かめてください。

内蔵モジュールの共通規約

SSPが内蔵している、名前がinternal_で始まるモジュールの共通の決まりです。(同じ名前のSAORI DLLと衝突しないように、internal_で始まる名前にしています。)
これらのモジュールはDirect SSTPからだけ呼べます。DLLとしてloadすることはできません。

関数と引数

Argument0が関数名(大文字小文字は区別しない)、Argument1以降がその関数の引数です。知らない関数名は400(unknown function)です。
引数は、関数ごとに決まった数の位置引数と、その後ろに並べるオプション(--名前=値)からなります。

  • 位置引数の位置にある値は、--で始まっていてもオプションとしては読まず、データとして扱います。
  • オプションは位置引数より後ろにだけ書けます。省略できる位置引数を飛ばしてオプションを書くときは、空の引数を置いてください。
  • オプションの名前は大文字小文字を区別しません。同じ名前を2回書くと後のものが有効です。
  • 位置引数より後ろにある--で始まらない値、関数が受け付けないオプションは400(invalid parameter)です。
  • どの関数も--async=識別子を受け付けます。
Argument0: Digest
Argument1: sha256
Argument2: SGVsbG8=
Argument3: --data-in=base64
Argument4: --out=base64

バイト列の入力の形式

ハッシュなどはバイト列を扱いますが、SAORIで渡せるのは文字列です。バイト列を受け取る引数にはそれぞれ名前(data、key、password、salt、ciphertext、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
パディング(=)はあってもなくてもよい。それぞれのアルファベット以外の文字は400(invalid encoding)。
base32
RFC 4648。大文字小文字・パディングの有無は問わない。不正な文字は400(invalid encoding)。
url
パーセントエンコードを戻したもの。+は空白に戻さない。%の後ろが16進2桁でなければ400(invalid encoding)。
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(大文字)にする。
text
--charset=の文字コード(既定はUTF-8)の文字列として返す。その文字コードとして正しく戻せないバイト列や、NUL・改行(SAORIのヘッダに書けない)を含むときは500(not text)。

--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。復号できない(暗号文の改ざん・鍵の違いなど)。
internal error
500。ベースウェア内部の想定外の失敗。

internal_data

データの形式の変換と、圧縮・展開。

Convert

データを別の形式で書き直す(base64⇔16進⇔パーセントエンコード⇔文字列など)。入力は--data-in=、出力は--out=で指定する。

Argument1
data(既定の形式はtext)
オプション
--data-in= / --out=(既定はtext) / --charset=

記述例

Argument0: Convert
Argument1: 48656c6c6f
Argument2: --data-in=hex
Argument3: --out=base64

→ Result: SGVsbG8=

  • 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バイト)+暗号文」。改ざんを検出できない。

鍵(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 / 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は空でもよい。
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$ソルト$ハッシュ

ソルトとハッシュはbase64(パディングなし)です。

  • 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)で照合できない。
これ以外の方式(bcryptなど)は400(unsupported algorithm)、形の誤りは400(invalid parameter)、パラメータがDeriveKeyの範囲の外なら400(out of range)。

Argument1
password(既定の形式はtext)
Argument2
PHC文字列
オプション
--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

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。整数のエポック秒なら書けます)。

ゾーン(--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
単位:second minute hour day week month year(複数形の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(大文字小文字は区別しない。これ以外は400 unsupported algorithm)
Argument2
文字列

記述例

Argument0: Normalize
Argument1: NFKC
Argument2: ガイド①

→ Result: ガイド1

  • SSP 2.9.13

互換モジュール

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