ファイルストレージ
ファイルデータを保存するため、EDHシステムはIPFS(Interplanetary File System)を活用し、ユーザーと組織が必要なデータを保存・バックアップできるようにします。
IPFSシステムはオープンなデータ配信システムのネットワークです。ファイルストレージサーバーはネットワークに参加して独自のファイルを保存できます。ユーザーはファイルの内容から生成されるコンテンツ識別子(CID)を介してファイルを取得できます。この方式により、特定のサーバーに依存せずにファイルURIを保存することが可能です。代わりにCIDで保存することで、ファイルを以前に保存していたサーバーがサービス停止した後もURLがデータにアクセスできないという問題を解決します。
S3 Backend (filebase.com)
In addition to uploading files directly to IPFS, EDH also supports storage via an S3-compatible API through filebase.com.
How it works:
- The application encrypts the file with NaCl secretbox (client-side)
- The encrypted file is uploaded via the S3 API to Filebase
- Filebase automatically pins the file to IPFS and returns a CID
- The application stores the CID and decryption key in EVFS metadata
Advantages:
- No need to maintain your own IPFS node
- Supports presigned URLs for uploads and downloads
- Files remain accessible via IPFS CID
- Suitable for applications that need simple HTTP-based uploads
End-to-End Encryption
IPFSネットワーク上に保存されたファイルはパブリックネットワーク上にあるため、保存されたファイルの読み取り権限に制限はありません。アーカイブされたファイルは暗号化する必要があります。その後、エンドツーエンドでファイルを復号化するための鍵共有に依存します。
読み取りキーの管理を容易にし、鍵の再設定をより効率的にするため、EDHはCryptreeベースのストレージ管理システムを使用しており、Ever File System(EVFS)に進化しています。
Cryptree暗号化
Cryptree形式でファイルを暗号化する特性は以下の通りです。
- ファイルの内容が書き込まれると、各ファイルが異なる暗号化キーを持つようにランダムな対称暗号化キー(KEY-C)が生成されます。
- キーはファイルのメタデータに保存されます。
- ファイルのメタデータが書き込まれると、メタデータは別のランダムキー(KEY-B)で暗号化されます。
- 開いているファイルはファイルオーナーディレクトリのメタデータに保存されます。
- ディレクトリのメタデータが書き込まれると、メタデータは別のランダム化されたキー(KEY-A)で暗号化されます。
キーの保存は上記のような階層的な方式で、ルートディレクトリまで続きます。
KEY-AKEY-BKEY-C1KEY-C2KEY-B'KEY-C3Ever File System(EVFS)
CID + key per childEver File System(EVFS)はCryptree原則に基づいたIPFSベースのファイル管理暗号化です。
- 対称キー暗号化でIPFS上にファイルコンテンツ(BLOB)を保存します。これは各ファイルコンテンツに新しくランダム化されたキーです。
- ファイルに関する情報をファイルノードに保存します。以下を含みます:
- ファイル名、ファイルタイプ、作成日などのファイルメタデータ情報
- ファイルコンテンツのIPFS CID
- ファイルコンテンツを復号化するために使用するキー。
- ファイルノードは対称キー暗号化でIPFS上に保存されます。各ノードに新しくランダム化されたキーです。
- ディレクトリノードにディレクトリに関する情報を保存します。以下を含みます:
- ディレクトリ名、作成日などのディレクトリメタデータ情報
- ディレクトリ内の子ノードのリスト。各ノードは以下を含みます:
- ノードタイプ(ディレクトリ/ファイル)
- メタデータノードのIPFS CID
- メタデータノードを復号化するために使用するキー。
- ディレクトリノードは対称キー暗号化でIPFS上に保存されます。各ノードに新しくランダム化されたキーです。
ファイルストレージの特性から、ユーザーが任意のディレクトリのキーにアクセスできる場合、そのディレクトリ以下のファイルとディレクトリのみが読み取れることがわかります。これにより、希望するサブディレクトリのキーのみを送ることで、ファイルへの部分的なアクセス管理が容易になります。
ファイルの失効は新しいキーを生成し、その後新しいキーで将来のファイルを暗号化することで行われます。つまり、新しいキーを持っていない人はキー変更後の新しいファイルコンテンツを読み取ることができません。
暗号化前のファイルノードコンテンツの例:
{
"name": "hello.txt",
"type": "file",
"block": {
"data": {
"/": "QmRpjfVxnZvrjDLKvzaGF4....."
},
"key": "bbfe62d3b458440a1b796d040f...."
}
}
エンコード前のディレクトリノードコンテンツの例:
{
"name": "",
"type": "directory",
"directories": [],
"files": [
{
"data": {
"/": "QmVAteSB3ywyKQkg8y......."
},
"key": "f6f77aeaab377409f9e......"
},
{
"data": {
"/": "QmfRdkKcgnnqaQTGRibwxdL......"
},
"key": "29a8e235c5abd65b964de...."
}
]
}
Supported File Types
EVFS can store any file type. The following medical file types have special rendering support:
| Type | Extension | MIME Type | Description |
|---|---|---|---|
| FHIR | .json | application/fhir+json | Structured health data |
| DICOM | .dcm | application/dicom | Medical imaging (CT, MRI, X-ray) |
| VCF | .vcf | text/x-vcard | Genomic variant data |
.pdf | application/pdf | Documents, reports, prescriptions | |
| JPEG | .jpg | image/jpeg | General photographs |
| PNG | .png | image/png | Device-captured images |
| TIFF | .tiff | image/tiff | High-resolution images |
| Text | .txt | text/plain | Notes, memos |
| ZIP | .zip | application/zip | Compressed multi-file archives |
For more details on medical standards, see Medical Data Standards.
ファイルストレージ
ファイルストレージに関する設定値を保存するため、EDHAccountコントラクトは以下のようにデータを保存できます:
アカウントストレージ:各アカウントについて、データは以下のようにアカウントのメインストレージに保存されます。
- ストレージオーナーアカウントのアカウントID
- URI:IPFS上のEVFSトップディレクトリのアドレス。
- RootKey:EVFSルートディレクトリを復号化するために使用するシークレットキー。アカウントの中央キーの現在のSEEDから計算できるSecretKeyで暗号化されています。
- フォーマット:
<24バイトnonce>|<nacl.secretboxデータ>
インターフェース:アカウント設定管理
contract EDHAccount {
bytes32 constant public KEY_ACCOUNT_STORE_URI = keccak256("io.evernetwork.edh.account.uri");
bytes32 constant public KEY_ACCOUNT_STORE_ROOT_KEY = keccak256("io.evernetwork.edh.account.key");
bytes32 constant public KEY_TAGGED_PUBLIC_KEY = keccak256("io.evernetwork.edh.tagged-public-key");
function setDataAddress(
bytes32 key, bytes32 publicKey, address value
) public onlyActiveDevice;
function setDataBytes(
bytes32 key, bytes32 publicKey, bytes memory value
) public onlyActiveDevice;
function setDataString(
bytes32 key, bytes32 publicKey, string memory value
) public onlyActiveDevice;
function setDataBytes32(
bytes32 key, bytes32 publicKey, bytes32 value
) public onlyActiveDevice;
function setDataUint(
bytes32 key, bytes32 publicKey, uint value
) public onlyActiveDevice;
function getDataAddress(bytes32 key) public view returns (bytes32 publicKey, address addressValue);
function getDataBytes(bytes32 key) public view returns (bytes32 publicKey, bytes memory bytesValue);
function getDataString(bytes32 key) public view returns (bytes32 publicKey, string memory stringValue);
function getDataBytes32(bytes32 key) public view returns (bytes32 publicKey, bytes32 bytes32Value);
function getDataUint(bytes32 key) public view returns (bytes32 publicKey, uint uintValue);
function getAddress(bytes32 key) public view returns (address);
function getBytes(bytes32 key) public view returns (bytes memory);
function getString(bytes32 key) public view returns (string memory stringValue);
function getBytes32(bytes32 key) public view returns (bytes32 bytes32Value);
function getUint(bytes32 key) public view returns (uint uintValue);
function getEncryptionKey(bytes32 key) public view returns (bytes32 keyValue);
function setEncryptionKey(bytes32 key, bytes32 value) public onlyActiveDevice;
function setAddress(bytes32 key, address value) public onlyActiveDevice;
function setBytes(bytes32 key, bytes memory value) public onlyActiveDevice;
function setString(bytes32 key, string memory value) public onlyActiveDevice;
function setBytes32(bytes32 key, bytes32 value) public onlyActiveDevice;
function setUint(bytes32 key, uint value) public onlyActiveDevice;
}
KEY_XXX : Account Storage URI変数とルートキーを保存するために使用するキーを表す定数です。
setDataXXX : キーで値を割り当てるトランザクション関数です。レースコンディションを防ぐためにアカウントの最新の公開鍵値が送られます。 その後、最後の変数とその時の公開鍵値を保存します。公開鍵またはシード依存の変数(例:公開鍵で暗号化された値)を保存するのに有用です。
getDataXXX : 指定されたキーとその変数の公開鍵に基づいて値を読み取るビュー関数です。
setXXX/getXXX : 公開鍵を確認せずにキーで値を読み取りまたは変更する関数です。
ファイル共有
ファイルを共有するには、ユーザーは共有したいディレクトリへのリンクと復号化キーを送ることでそれを行えます。これはさまざまな方法で行うことができ、各チャンネルには以下のような長所と短所があります。
- ディレクトリのCIDとキーを直接共有する - この場合、ユーザーは共有したいディレクトリのCIDとその暗号化キーを直接送ることができます。QRコードやEmailなどのチャンネルを通じて。この方法では、CIDとキーを受け取った人はシステムにファイルが保存されている限り、いつでもファイルにアクセスできます。共有時点のスナップショットファイルになります。
- EverのFile Viewer Webリンク共有 - EverはEDH上のファイルを読み取るためのファイルビューアWebサービスを提供しており、ユーザーはファイルへのアクセス時間制限を設定でき、Everのシステムはファイルアクセス履歴を記録できます。ただし、このチャンネルはEverの追加サービスのみです。
- コミュニティの他の開発者によって提供される他の方法とチャンネル。
Limitations and Performance
| Item | Details |
|---|---|
| Encryption overhead | NaCl secretbox adds ~40 bytes per block (24-byte nonce + 16-byte MAC) |
| IPFS Pinning | Files must be pinned on at least one IPFS node to persist on the network |
| Content Addressing | Files with identical content (after encryption) will have different CIDs because their encryption keys differ |
| Immutability | Files on IPFS cannot be modified -- updates create a new CID |
For large files (e.g., DICOM series), it is recommended to use the S3/Filebase backend, which handles large uploads via presigned URLs more effectively than uploading directly through IPFS.
See Also
- Account -- EDHAccount used to store storage references
- Device -- Key derivation and SEED management
- Medical Data Standards -- FHIR, DICOM, VCF
- Ever Services -- S3/Filebase storage and File Viewer