schemapin
MCPラグプル攻撃を防ぐための暗号ツールスキーマ検証機能です。ECDSA P-256署名、SHA-256ハッシング、TOFUキーピンニング、.well-knownディスカバリー、署名付き失効ドキュメント、署名の有効期限切れ(v1.4-alpha版は全4言語対応)、DNS TXTクロス検証、スキーマバージョンバインディング(系統チェーン)に対応しています。これらの機能により、エージェント間の通信の真正性と完全性を確保し、セキュアな取引環境を実現できます。
description の原文を見る
Cryptographic tool schema verification to prevent MCP Rug Pull attacks — ECDSA P-256 signing, SHA-256 hashing, TOFU key pinning, .well-known discovery, signed revocation documents, and (v1.4-alpha across all four languages) signature expiration, DNS TXT cross-verification, and schema version binding (lineage chain)
SKILL.md 本文
SchemaPin 開発スキル ガイド
目的: このガイドは、AI アシスタントが暗号化ツール スキーマ検証のために SchemaPin をアプリケーションに迅速に統合するのに役立ちます。
詳細なドキュメント: README、技術仕様、および各サブディレクトリ内の言語別 README を参照してください。
SchemaPin の機能
SchemaPin は、開発者がツール スキーマに暗号署名 (ECDSA P-256 + SHA-256) を施し、クライアントがスキーマが改ざんされていないことを検証できるようにすることで、"MCP Rug Pull" 攻撃を防ぎます。Trust-On-First-Use (TOFU) キー ピニングと RFC 8615 .well-known エンドポイントを公開鍵の検出に使用します。
ThirdKey トラスト スタックの一部: SchemaPin (ツール整合性) → AgentPin (エージェント ID) → Symbiont (ランタイム)
言語別クイックスタート
Python
pip install schemapin
from schemapin.crypto import KeyManager, SignatureManager
from schemapin.core import SchemaPinCore
# キーペアを生成
private_key, public_key = KeyManager.generate_keypair()
# スキーマに署名
core = SchemaPinCore()
canonical = core.canonicalize_schema(schema_dict)
signature = SignatureManager.sign_schema(private_key, canonical)
# 検証
is_valid = SignatureManager.verify_signature(public_key, canonical, signature)
JavaScript
npm install schemapin
import { KeyManager, SignatureManager, SchemaPinCore } from 'schemapin';
// キーペアを生成
const { privateKey, publicKey } = KeyManager.generateKeypair();
// スキーマに署名
const core = new SchemaPinCore();
const canonical = core.canonicalizeSchema(schema);
const signature = await SignatureManager.signSchema(privateKey, canonical);
// 検証
const isValid = await SignatureManager.verifySignature(publicKey, canonical, signature);
Go
go get github.com/ThirdKeyAi/schemapin/go@v1.3.0
import (
"github.com/ThirdKeyAi/schemapin/go/pkg/core"
"github.com/ThirdKeyAi/schemapin/go/pkg/crypto"
)
// キーペアを生成
km := crypto.NewKeyManager()
privKey, pubKey, _ := km.GenerateKeypair()
// スキーマに署名
spc := core.NewSchemaPinCore()
canonical, _ := spc.CanonicalizeSchema(schema)
sig, _ := crypto.NewSignatureManager().SignSchema(privKey, canonical)
// 検証
valid, _ := crypto.NewSignatureManager().VerifySignature(pubKey, canonical, sig)
Rust
[dependencies]
schemapin = "1.3"
use schemapin::crypto::{generate_key_pair, sign_data, verify_signature};
use schemapin::core::SchemaPinCore;
// キーペアを生成
let key_pair = generate_key_pair()?;
// 署名
let core = SchemaPinCore::new();
let canonical = core.canonicalize_schema(&schema)?;
let signature = sign_data(&key_pair.private_key_pem, &canonical)?;
// 検証
let is_valid = verify_signature(&key_pair.public_key_pem, &canonical, &signature)?;
コア概念
1. スキーマ正規化
スキーマは、ハッシング前に正規化 (ソート済みキーを使用した確定的 JSON シリアライゼーション) されます。これにより、同一のスキーマが常に同じハッシュを生成することが保証されます。キーの順序に関わらず。
2. .well-known ディスカバリー
開発者は公開鍵を https://example.com/.well-known/schemapin.json で公開します:
from schemapin.utils import create_well_known_response
response = create_well_known_response(
public_key_pem=public_key_pem,
developer_name="Acme Corp",
schema_version="1.2",
revocation_endpoint="https://example.com/.well-known/schemapin-revocations.json"
)
3. TOFU キー ピニング
最初の検証時に、開発者の公開鍵フィンガープリントが固定されます。その後の検証では、同じドメインに対して異なるキーを拒否します — キー置換攻撃を検出します。
4. 検証ワークフロー
オンライン (標準):
workflow = SchemaVerificationWorkflow(pin_store)
result = workflow.verify_schema(schema, signature, "https://example.com")
オフライン (v1.2.0 — HTTP は不要):
from schemapin.verification import verify_schema_offline, KeyPinStore
pin_store = KeyPinStore()
result = verify_schema_offline(
schema, signature_b64, domain, tool_id,
discovery_data, revocation_doc, pin_store
)
v1.2.0 の機能
スタンドアロン失効ドキュメント
運用プレイブック、すべての4言語、インライン + スタンドアロン統合セマンティクスを含む完全ガイド: docs/revocation.md。
from schemapin.revocation import (
build_revocation_document, add_revoked_key,
check_revocation, check_revocation_combined, RevocationReason
)
doc = build_revocation_document("example.com")
add_revoked_key(doc, fingerprint, RevocationReason.KEY_COMPROMISE)
check_revocation(doc, some_fingerprint) # 失効している場合は例外を発生
# 本番環境では、常に統合チェック (インラインリスト + スタンドアロン ドキュメント) を使用します:
check_revocation_combined(
revoked_keys_list=discovery.revoked_keys,
revocation_doc=doc,
fingerprint=some_fingerprint,
)
トラスト バンドル (オフライン / エアギャップ)
インターネットがない環境用に、ディスカバリー + 失効データを事前にパッケージ化します:
from schemapin.bundle import SchemaPinTrustBundle
bundle = SchemaPinTrustBundle.from_json(bundle_json_str)
discovery = bundle.find_discovery("example.com")
revocation = bundle.find_revocation("example.com")
プラガブル ディスカバリー リゾルバー
from schemapin.resolver import (
WellKnownResolver, # HTTP .well-known ルックアップ
LocalFileResolver, # ローカル JSON ファイル
TrustBundleResolver, # インメモリ トラスト バンドル
ChainResolver, # ファーストマッチ フォールスルー
)
# チェーン: バンドルを最初に試し、HTTP にフォールバック
resolver = ChainResolver([
TrustBundleResolver.from_json(bundle_json),
WellKnownResolver(timeout=10),
])
リゾルバー ベースの検証
from schemapin.verification import verify_schema_with_resolver
result = verify_schema_with_resolver(
schema, signature_b64, domain, tool_id,
resolver, pin_store
)
v1.3.0 の機能
SkillSigner — ファイル ベースのスキル フォルダー署名
スキル ディレクトリ全体 (例: SKILL.md を含むフォルダー) に ECDSA P-256 で署名します。ファイルの横に .schemapin.sig マニフェストを生成し、ファイルが改ざんされていないことを証明します。
Python:
from schemapin.skill import sign_skill, verify_skill_offline
# スキル ディレクトリに署名
sig = sign_skill("./my-skill/", private_key_pem, "example.com")
# ./my-skill/ に .schemapin.sig を書き込みます
# オフラインで検証
from schemapin.verification import KeyPinStore
result = verify_skill_offline("./my-skill/", discovery_data, sig, revocation_doc, KeyPinStore())
JavaScript:
import { signSkill, verifySkillOffline } from 'schemapin/skill';
const sig = await signSkill('./my-skill/', privateKeyPem, 'example.com');
const result = verifySkillOffline('./my-skill/', discoveryData, sig, revDoc, pinStore);
Go:
import "github.com/ThirdKeyAi/schemapin/go/pkg/skill"
sig, err := skill.SignSkill("./my-skill/", privateKeyPEM, "example.com", "", "")
result := skill.VerifySkillOffline("./my-skill/", disc, sig, rev, pinStore, "")
Rust:
use schemapin::skill::{sign_skill, verify_skill_offline};
let sig = sign_skill("./my-skill/", &private_key_pem, "example.com", None, None)?;
let result = verify_skill_offline("./my-skill/", &disc, Some(&sig), rev.as_ref(), Some(&pin_store), None);
.schemapin.sig 形式
{
"schemapin_version": "1.3",
"skill_name": "my-skill",
"skill_hash": "sha256:<root_hash>",
"signature": "<base64_ecdsa_signature>",
"signed_at": "2026-02-14T00:00:00Z",
"domain": "example.com",
"signer_kid": "sha256:<key_fingerprint>",
"file_manifest": {
"SKILL.md": "sha256:<file_hash>"
}
}
改ざん検出
from schemapin.skill import detect_tampered_files, canonicalize_skill
_, current_manifest = canonicalize_skill("./my-skill/")
tampered = detect_tampered_files(current_manifest, sig.file_manifest)
# tampered.modified, tampered.added, tampered.removed
v1.4.0-alpha.2 の機能 (すべての4言語 — プレビュー)
スキーマ バージョン バインディング (schema_version + previous_hash)
.schemapin.sig 上の2つの付加的なオプショナル フィールドにより、パブリッシャーが系統を宣言し、ベリファイアーがそれを強制する方法を提供します。攻撃者が同じ名前の下で改ざんされたスキーマを置き換える rug-pull 置換に対して防御します。
Rust:
use schemapin::skill::{sign_skill_with_options, SignOptions, verify_chain};
let v1 = sign_skill_with_options(&dir_v1, &priv_pem, "example.com",
SignOptions::new().with_schema_version("1.0.0"))?;
let v2 = sign_skill_with_options(&dir_v2, &priv_pem, "example.com",
SignOptions::new()
.with_schema_version("1.1.0")
.with_previous_hash(&v1.skill_hash))?;
verify_chain(&v2, &v1)?; // 純粋なメタデータ チェック; 両方はすでに暗号化検証されている必要があります。
Python:
from schemapin.skill import SkillSigner, SignOptions, verify_chain
v2 = SkillSigner.sign_with_options(dir_v2, priv_pem, "example.com",
SignOptions(schema_version="1.1.0", previous_hash=v1["skill_hash"]))
verify_chain(v2, v1)
JavaScript:
import { signSkillWithOptions, verifyChain } from 'schemapin';
const v2 = signSkillWithOptions(dirV2, privPem, 'example.com',
{ schemaVersion: '1.1.0', previousHash: v1.skill_hash });
verifyChain(v2, v1);
Go:
v2, _ := skill.SignSkillWithOptions(dirV2, privPEM, "example.com", skill.SignOptions{
SchemaVersion: "1.1.0",
PreviousHash: v1.SkillHash,
})
err := skill.VerifyChain(v2, v1)
VerificationResult は schema_version と previous_hash フィールドを獲得します — ベリファイアーはこれらを情報として表示します。チェーン強制は verify_chain (または言語相当) を通じてオプトインです。ChainError は2つの障害モード を区別します: no_previous_hash (現在の署名がフィールドを欠いている) と mismatch (両方存在するが不相等 — 置換の可能性)。
完全ガイド: docs/schema-version-binding.md。
v1.4.0-alpha.1 の機能 (すべての4言語 — プレビュー)
両方の機能は 付加的なオプショナル フィールド/レコード です: v1.3 ベリファイアーはそれらを無視し、これらのフィールドなしの v1.4 署名は v1.3 と同一に動作します。Python、JavaScript、および Go ポートは、v1.4.0 リリース前の後続のアルファで続きます。
署名有効期限 (expires_at)
.schemapin.sig 上のオプショナル ISO 8601 / RFC 3339 タイムスタンプ。ベリファイアーがタイムスタンプを過ぎると、結果が低下します (警告) 失敗の代わりに — valid は true のままで、expired は true になり、signature_expired 警告が追加されます。
use schemapin::skill::{sign_skill_with_options, SignOptions, verify_skill_offline};
// 180 日の TTL で署名 — .schemapin.sig に expires_at を書き込みます
let opts = SignOptions::new().with_expires_in(chrono::Duration::days(180));
let sig = sign_skill_with_options(dir, &priv_pem, "example.com", opts)?;
// 検証 — expires_at を過ぎると valid=true で expired=true と
// "signature_expired" 警告を返します。ハード失敗は決してありません
let result = verify_skill_offline(dir, &discovery, None, None, None, Some("tool"));
if result.expired {
log::warn!("signature past {}", result.expires_at.unwrap_or_default());
}
SignOptions はビルダーです; レガシー sign_skill(...) 関数は v1.3 呼び出し元のために保持され、expires_at なしで署名を書き込みます (依然として schemapin_version: "1.3" として広告)。
DNS TXT クロス検証 (_schemapin.{domain})
ツール プロバイダーは .well-known/schemapin.json の横に TXT レコードを公開 できます:
_schemapin.example.com. IN TXT "v=schemapin1; kid=acme-2026-04; fp=sha256:a1b2c3..."
ディスカバリー キーに対して TXT をクロスチェックすることで、セカンド チャネル 検証を与えます — DNS 認証チェーンは HTTPS ホスティング チェーンから独立しています。不一致はハード失敗です (DOMAIN_MISMATCH); レコードがない場合はノーオペレーションです。
use schemapin::dns::{parse_txt_record, fetch_dns_txt};
use schemapin::skill::verify_skill_offline_with_dns;
// パーサー/マッチャーは常に利用可能です
let txt = parse_txt_record("v=schemapin1; fp=sha256:a1b2c3...")?;
// 非同期フェッチは `dns` Cargo フィーチャー (hickory-resolver) の背後に存在します
#[cfg(feature = "dns")]
let txt = fetch_dns_txt("example.com").await?;
let result = verify_skill_offline_with_dns(
dir, &discovery, None, None, None, Some("tool"), txt.as_ref(),
);
| Cargo フィーチャー | デフォルト | 含まれるもの |
|---|---|---|
fetch | オフ | reqwest, tokio, async-trait |
dns (新規) | オフ | hickory-resolver, tokio, async-trait |
サーバー側のセットアップ
.well-known エンドポイントの公開
Python CLI ツールが含まれています:
# キーペアを生成
schemapin-keygen --output-dir ./keys
# スキーマに署名
schemapin-sign --key ./keys/private.pem --schema schema.json
# 署名を検証
schemapin-verify --key ./keys/public.pem --schema schema.json --signature sig.b64
Go CLI 相当物も利用可能です (go install github.com/ThirdKeyAi/schemapin/go/cmd/...@v1.3.0)。
アーキテクチャ
開発者 クライアント (AI プラットフォーム)
─────────────────────────────────────────────────────────────
1. ECDSA P-256 キーペアを生成
2. 公開鍵を 3. 公開鍵を
/.well-known/schemapin.json /.well-known/schemapin.json
で公開 から検出
4. ツール スキーマに署名 5. 署名を検証
(正規化 → SHA-256 (正規化 → SHA-256
→ ECDSA 署名) → ECDSA 検証)
6. キー フィンガープリントを TOFU ピン
7. 失効状態をチェック
言語別 API リファレンス
| 操作 | Python | JavaScript | Go | Rust |
|---|---|---|---|---|
| キー生成 | KeyManager.generate_keypair() | KeyManager.generateKeypair() | km.GenerateKeypair() | generate_key_pair() |
| スキーマ署名 | SignatureManager.sign_schema() | SignatureManager.signSchema() | sm.SignSchema() | sign_data() |
| 署名検証 | SignatureManager.verify_signature() | SignatureManager.verifySignature() | sm.VerifySignature() | verify_signature() |
| 正規化 | SchemaPinCore().canonicalize_schema() | new SchemaPinCore().canonicalizeSchema() | spc.CanonicalizeSchema() | SchemaPinCore::new().canonicalize_schema() |
| キー検出 | PublicKeyDiscovery.fetch_well_known() | PublicKeyDiscovery.fetchWellKnown() | FetchWellKnown() | WellKnownResolver (fetch フィーチャー) |
| オフライン検証 | verify_schema_offline() | verifySchemaOffline() | VerifySchemaOffline() | verify_schema_offline() |
| リゾルバー検証 | verify_schema_with_resolver() | verifySchemaWithResolver() | VerifySchemaWithResolver() | verify_schema_with_resolver() |
| スキル フォルダー署名 | sign_skill() | signSkill() | skill.SignSkill() | sign_skill() |
| スキル検証 | verify_skill_offline() | verifySkillOffline() | skill.VerifySkillOffline() | verify_skill_offline() |
| 改ざん検出 | detect_tampered_files() | detectTamperedFiles() | skill.DetectTamperedFiles() | detect_tampered_files() |
テスト
# Python
cd python && python -m pytest tests/ -v
# JavaScript
cd javascript && npm test
# Go
cd go && go test ./...
# Rust
cd rust && cargo test
AI アシスタントのための Pro ヒント
- 常に正規化してから 署名または検証します — 生の JSON 比較は失敗します
- 事前取得されたディスカバリー データがある場合はオフライン検証を使用します — スキーマ検証中の HTTP コールを回避します
- トラスト バンドルは CI/CD およびエアギャップ デプロイメントに理想的です
- ChainResolver でリゾルバーをレイアー化できます: バンドル → ローカル ファイル → HTTP としてフォールバック
- TOFU ピニングは ドメイン用に見られた最初のキーが信頼されることを意味します — キー変更時はユーザーに警告します
- すべての言語は同じ暗号を使用します — ECDSA P-256 + SHA-256、したがってクロス言語検証が機能します
- 失効チェックは 常に実行されるべきです — シンプルなリストとスタンドアロン ドキュメント両方
- SkillSigner はディレクトリ全体に署名します — ClaWHub などのレジストリにアップロードされた SKILL.md フォルダーに理想的です
.schemapin.sigはハッシング から自動的に除外されます — 古い署名を削除せずにディレクトリを再署名できます
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- ThirdKeyAI
- リポジトリ
- ThirdKeyAI/SchemaPin
- ライセンス
- MIT
- 最終更新
- 2026/5/8
Source: https://github.com/ThirdKeyAI/SchemaPin / ライセンス: MIT