A blockchain powered file sharing platform with client-side encryption, Shamir's Secret Sharing, and IPFS storage zero single point of failure by design.
BlockSafe is a decentralized file sharing system that eliminates the trust placed in any single server or administrator. Files are encrypted in the browser before upload, stored on IPFS via Pinata, and their encryption keys are split across three independent nodes using Shamir's Secret Sharing — so no single node can ever reconstruct your key alone.
Access control is enforced entirely on-chain through two Solidity smart contracts deployed on Ethereum, making permissions transparent, tamper-proof, and auditable by anyone.
- Client-side AES-256-GCM encryption — files are encrypted in the browser before leaving your device; the server never sees plaintext.
- Shamir's Secret Sharing (2-of-3 threshold) — the encryption key is split into three shares distributed across independent nodes (alpha, beta, gamma); any two shares reconstruct the key.
- Blockchain access control —
FileRegistryandGroupRegistrysmart contracts manage who can access what, with on-chain permission grants and revocations. - IPFS storage via Pinata — encrypted file blobs are stored on IPFS; only the CID is recorded on-chain.
- MetaMask wallet authentication — sign-in with Ethereum (SIWE) using cryptographic signatures; no passwords, no centralized identity provider.
- Time-based access expiry — share files with a deadline; permissions expire automatically at the contract level.
- Group / Organization support — create teams, invite members by wallet address, and upload files scoped to a group with inherited access.
- Proxy re-encryption for sharing — when sharing with another user, nodes re-encrypt key shares under the recipient's public key without ever exposing the plaintext key.
- Public file links — optionally generate a public link allowing anyone to access a file without a wallet.
┌─────────────┐ AES-GCM encrypted blob ┌──────────────┐
│ Browser │ ─────────────────────────────────► │ IPFS/Pinata │
│ (Next.js) │ ◄── CID ────────────────────────── └──────────────┘
└──────┬──────┘
│ register file + encrypted shares
▼
┌────────────────────┐
│ Ethereum Network │
│ ┌──────────────┐ │
│ │ FileRegistry │ │ — stores CID, owner, group, encrypted key shares
│ └──────────────┘ │
│ ┌───────────────┐ │
│ │ GroupRegistry │ │ — manages organizations, roles, invitations
│ └───────────────┘ │
└────────────────────┘
│
│ encrypted key shares (one per node)
▼
┌──────────────────────────────────┐
│ Shamir Nodes (Next.js API) │
│ Node Alpha | Node Beta | Node γ │ — each holds one encrypted share
└──────────────────────────────────┘
│ 2-of-3 shares → reconstruct AES key → decrypt file
▼
┌─────────────┐
│ Browser │ — decrypts and renders the file locally
└─────────────┘
| Layer | Technology |
|---|---|
| Frontend | Next.js 15, React 19, Tailwind CSS v4, shadcn/ui, Framer Motion |
| Blockchain | Solidity 0.8.28, Hardhat, ethers.js v6 |
| Storage | IPFS via Pinata SDK |
| Encryption | Web Crypto API (AES-GCM 256), eth-crypto, @metamask/eth-sig-util |
| Secret Sharing | Custom GF(256) Shamir implementation (secrets.js-grempe) |
| Auth | Sign-in with Ethereum (SIWE), JWT, MongoDB (nonce store) |
| Database | MongoDB / Mongoose |
BlockSafe/
├── contracts/
│ ├── FileRegistry.sol # Core file metadata & access control
│ └── GroupRegistry.sol # Organization & membership management
├── scripts/
│ ├── deploy.js # Deploy both contracts & save addresses
│ ├── generate-node-keys.js # Generate keypairs for Shamir nodes
│ └── derive_keys.js # Key derivation utilities
├── src/
│ ├── app/
│ │ ├── (app)/
│ │ │ ├── dashboard/ # Main file dashboard
│ │ │ ├── my-files/ # User's uploaded files
│ │ │ ├── shared-with-me/# Files shared by others
│ │ │ ├── organizations/ # Group management UI
│ │ │ └── file/[id]/ # File detail & access page
│ │ ├── api/
│ │ │ ├── auth/ # SIWE request-message & verify-signature
│ │ │ ├── files/upload/ # Pinata upload endpoint
│ │ │ ├── encrypt-shares/# Share encryption for upload
│ │ │ └── nodes/reencrypt/ # Proxy re-encryption for file sharing
│ │ └── public/[fileId]/ # Public file access (no wallet required)
│ ├── components/
│ │ ├── FileUploadDialog.jsx
│ │ ├── ShareFileDialog.jsx
│ │ ├── GroupManagement.jsx
│ │ ├── EncryptionKeyCard.jsx
│ │ └── CreatePublicLinkDialog.jsx
│ └── lib/
│ ├── shamirSecretSharing.js # Custom GF(256) SSS implementation
│ ├── encryption.js # AES-GCM encrypt/decrypt helpers
│ ├── contract.js # ethers.js contract bindings
│ ├── nodeConfig.js # Shamir node public keys
│ └── publicKeyRegistry.js # On-chain public key helpers
└── ignition/modules/
└── FileRegistry.js # Hardhat Ignition deploy module
- Node.js 18+
- MetaMask browser extension
- A Pinata account (for IPFS uploads)
- MongoDB instance (local or Atlas)
git clone https://github.com/your-username/BlockSafe.git
cd BlockSafe
npm installCreate a .env.local file in the project root:
# Pinata (IPFS)
PINATA_JWT=your_pinata_jwt_token
PINATA_GATEWAY=your_pinata_gateway_url
# MongoDB
MONGODB_URI=mongodb://localhost:27017/blocksafe
# JWT for API auth
JWT_SECRET=your_jwt_secret_key
# Shamir Node Private Keys (generate with scripts/generate-node-keys.js)
NODE_ALPHA_PRIVATE_KEY=0x...
NODE_BETA_PRIVATE_KEY=0x...
NODE_GAMMA_PRIVATE_KEY=0x...node scripts/generate-node-keys.jsCopy the generated public keys into src/lib/nodeConfig.js and the private keys into .env.local.
Start a local Hardhat node:
npx hardhat nodeIn a separate terminal, deploy the contracts:
node scripts/deploy.jsContract addresses are automatically saved to src/lib/contract-config.json.
npm run devOpen http://localhost:3000 and connect your MetaMask wallet.
- Encrypt — the browser generates a random AES-256-GCM key and encrypts the file locally.
- Split — the raw key bytes are split into 3 shares via Shamir's Secret Sharing (GF(256), threshold = 2).
- Encrypt shares — each share is encrypted with the corresponding node's public key (x25519-xsalsa20-poly1305).
- Upload — the encrypted file blob is sent to Pinata; the returned IPFS CID is recorded.
- Register —
FileRegistry.registerFile()is called on-chain with the CID, filename, group ID, and the three encrypted shares stored in contract storage.
- Verify access — the contract's
hasValidAccess()is checked for the requesting wallet. - Fetch shares — the user calls
getFileShares()on-chain to retrieve the encrypted shares. - Request re-encryption — the user's browser calls
/api/nodes/reencrypton at least 2 nodes, providing a signed message as proof of identity. - Node validates — each node verifies the Ethereum signature, decrypts its share with its private key, and re-encrypts it for the user's public key.
- Reconstruct key — the browser decrypts the two re-encrypted shares and feeds them into
combine()to reconstruct the AES key. - Decrypt file — the encrypted IPFS blob is fetched and decrypted locally in the browser.
| Function | Description |
|---|---|
registerFile(cid, filename, groupId, shares) |
Upload a new file and register it on-chain |
grantAccess(fileId, recipient, level, wrappedKey, expiresAt) |
Share a file with time-optional expiry |
revokeAccess(fileId, user) |
Revoke a previously granted permission |
hasValidAccess(fileId, user) |
Check current access (owner / granted / group) |
getFileShares(fileId) |
Retrieve encrypted key shares (access-gated) |
registerPublicKey(publicKey) |
Register your encryption public key on-chain |
updateFile(fileId, newCid) |
Update a file's IPFS CID (owner only) |
deleteFile(fileId) |
Soft-delete a file (owner only) |
| Function | Description |
|---|---|
createGroup(name) |
Create an organization; caller becomes Admin |
inviteMember(groupId, user) |
Invite a wallet address (Admin only) |
acceptInvite(groupId) |
Accept a pending invitation |
isMember(groupId, user) |
Check active membership |
| Threat | Mitigation |
|---|---|
| Server compromise | Files are AES-256-GCM encrypted client-side; server stores only ciphertext |
| Single node compromise | Shamir 2-of-3 — one compromised node reveals nothing |
| Unauthorized access | On-chain access control enforced by smart contract; cannot be bypassed |
| Replay attacks | SIWE nonce rotated after each login; node re-encryption requires fresh signature |
| Centralized IPFS gateway | CIDs are content-addressed; files can be fetched from any IPFS gateway |