feat: Add implementation details and dependencies
- Added a detailed implementation status report (impl.md). - Added the `@noble/hashes` dependency for Blake3 hashing. - Updated package.json and pnpm-lock.yaml accordingly. - Created a new component for Blake3 hashing demo.
This commit is contained in:
parent
2c16c8bc11
commit
773fd2e2bf
69
impl.md
Normal file
69
impl.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Implementation Status Report
|
||||||
|
|
||||||
|
## Analysis of Implementation Status
|
||||||
|
|
||||||
|
### What Has Been Implemented:
|
||||||
|
|
||||||
|
1. **Basic Website Structure**:
|
||||||
|
- Svelte-based frontend with components for layout, navigation, and content display
|
||||||
|
- Responsive design with mobile support
|
||||||
|
- Markdown content rendering capability
|
||||||
|
|
||||||
|
2. **IPFS Integration**:
|
||||||
|
- IPFS service implementation using Helia library
|
||||||
|
- Content retrieval from IPFS
|
||||||
|
- Content upload to IPFS
|
||||||
|
- Fallback to IPFS gateways when direct connection fails
|
||||||
|
- Local caching of IPFS content using IndexedDB
|
||||||
|
- Network status monitoring for offline support
|
||||||
|
|
||||||
|
3. **UI Components**:
|
||||||
|
- Header/Navbar
|
||||||
|
- Sidebar navigation
|
||||||
|
- Footer
|
||||||
|
- Markdown content display
|
||||||
|
- IPFS demo functionality
|
||||||
|
|
||||||
|
4. **Demo Functionality**:
|
||||||
|
- IPFS content upload and retrieval demo
|
||||||
|
- Mock functionality when IPFS is not available
|
||||||
|
|
||||||
|
### What Is Missing:
|
||||||
|
|
||||||
|
1. **Content Processing Pipeline**:
|
||||||
|
- No implementation of the Blake hashing algorithm for content
|
||||||
|
- No implementation of content encryption/decryption
|
||||||
|
- No CLI tools or scripts for processing content files as described in specs
|
||||||
|
- Missing the pipeline for file discovery, normalization, hashing, encryption, and IPFS upload
|
||||||
|
|
||||||
|
2. **Metadata Structure**:
|
||||||
|
- No implementation of the metadata structure as described in specs
|
||||||
|
- Missing the pages list metadata with Blake hash + IPFS CID format
|
||||||
|
- No implementation of metadata retrieval from IPFS
|
||||||
|
|
||||||
|
3. **Security Features**:
|
||||||
|
- Missing the encryption/decryption functionality using Blake hash as key
|
||||||
|
- No implementation of the security considerations mentioned in specs
|
||||||
|
|
||||||
|
4. **Collection Structure**:
|
||||||
|
- No implementation of the collection structure as described in specs
|
||||||
|
- Missing the `.collection` file handling
|
||||||
|
|
||||||
|
5. **Content Retrieval with Decryption**:
|
||||||
|
- No implementation of retrieving encrypted content and decrypting it
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The project has implemented a solid foundation for a browser-based website with IPFS integration, including:
|
||||||
|
- Basic website structure and UI components
|
||||||
|
- IPFS content retrieval and upload
|
||||||
|
- Local caching and offline support
|
||||||
|
- Demo functionality
|
||||||
|
|
||||||
|
However, the core security features described in the specifications are not implemented:
|
||||||
|
- The content processing pipeline with Blake hashing and encryption
|
||||||
|
- The metadata structure with combined Blake hash and IPFS CID
|
||||||
|
- The collection structure for organizing content
|
||||||
|
- The encryption/decryption functionality
|
||||||
|
|
||||||
|
The current implementation provides a working demo of IPFS integration but lacks the security features that are central to the project's specifications. To fully meet the requirements, the missing components would need to be implemented, particularly the content processing pipeline with encryption and the metadata structure.
|
@ -31,6 +31,7 @@
|
|||||||
"@libp2p/webrtc": "^5.2.12",
|
"@libp2p/webrtc": "^5.2.12",
|
||||||
"@libp2p/websockets": "^9.2.10",
|
"@libp2p/websockets": "^9.2.10",
|
||||||
"@libp2p/webtransport": "^5.0.40",
|
"@libp2p/webtransport": "^5.0.40",
|
||||||
|
"@noble/hashes": "^1.8.0",
|
||||||
"@tailwindcss/postcss": "^4.1.6",
|
"@tailwindcss/postcss": "^4.1.6",
|
||||||
"@tailwindcss/vite": "^4.1.6",
|
"@tailwindcss/vite": "^4.1.6",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
|
4919
sweb/pnpm-lock.yaml
4919
sweb/pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
198
sweb/src/components/BlakeHashDemo.svelte
Normal file
198
sweb/src/components/BlakeHashDemo.svelte
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cryptoService } from '../services/crypto.service';
|
||||||
|
import { contentProcessorService } from '../services/content-processor.service';
|
||||||
|
import { ipfsService } from '../services/ipfs.service';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let textInput = '';
|
||||||
|
let blakeHash = '';
|
||||||
|
let selectedFile: File | null = null;
|
||||||
|
let fileBlakeHash = '';
|
||||||
|
let normalizedFilename = '';
|
||||||
|
let ipfsCid = '';
|
||||||
|
let combinedKey = '';
|
||||||
|
let isProcessing = false;
|
||||||
|
let errorMessage = '';
|
||||||
|
let successMessage = '';
|
||||||
|
|
||||||
|
// Hash the text input using Blake3
|
||||||
|
function hashText() {
|
||||||
|
try {
|
||||||
|
errorMessage = '';
|
||||||
|
if (!textInput.trim()) {
|
||||||
|
errorMessage = 'Please enter some text to hash';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
blakeHash = cryptoService.hashStringBlake3AsHex(textInput);
|
||||||
|
successMessage = 'Text hashed successfully!';
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error hashing text:', error);
|
||||||
|
errorMessage = `Error hashing text: ${error instanceof Error ? error.message : String(error)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle file selection
|
||||||
|
function handleFileSelect(event: Event) {
|
||||||
|
const input = event.target as HTMLInputElement;
|
||||||
|
if (input.files && input.files.length > 0) {
|
||||||
|
selectedFile = input.files[0];
|
||||||
|
// Reset previous results
|
||||||
|
fileBlakeHash = '';
|
||||||
|
normalizedFilename = '';
|
||||||
|
ipfsCid = '';
|
||||||
|
combinedKey = '';
|
||||||
|
errorMessage = '';
|
||||||
|
successMessage = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the selected file
|
||||||
|
async function processFile() {
|
||||||
|
if (!selectedFile) {
|
||||||
|
errorMessage = 'Please select a file to process';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
isProcessing = true;
|
||||||
|
errorMessage = '';
|
||||||
|
successMessage = '';
|
||||||
|
|
||||||
|
// Normalize the filename
|
||||||
|
normalizedFilename = contentProcessorService.normalizeFilename(selectedFile.name);
|
||||||
|
|
||||||
|
// Hash the file using Blake3
|
||||||
|
fileBlakeHash = await cryptoService.hashFileBlake3AsHex(selectedFile);
|
||||||
|
|
||||||
|
// Process the file through the content processing pipeline
|
||||||
|
const metadata = await contentProcessorService.processFile(selectedFile);
|
||||||
|
|
||||||
|
// Update the UI with the results
|
||||||
|
ipfsCid = metadata.ipfsCid;
|
||||||
|
combinedKey = metadata.combinedKey;
|
||||||
|
|
||||||
|
isProcessing = false;
|
||||||
|
successMessage = 'File processed successfully!';
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error processing file:', error);
|
||||||
|
errorMessage = `Error processing file: ${error instanceof Error ? error.message : String(error)}`;
|
||||||
|
isProcessing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize IPFS when the component mounts
|
||||||
|
onMount(async () => {
|
||||||
|
try {
|
||||||
|
await ipfsService.initialize();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initializing IPFS:', error);
|
||||||
|
errorMessage = `Error initializing IPFS: ${error instanceof Error ? error.message : String(error)}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="p-6 max-w-4xl mx-auto bg-white rounded-lg shadow-md">
|
||||||
|
<h2 class="text-2xl font-bold mb-6">Blake Hashing Demo</h2>
|
||||||
|
|
||||||
|
<!-- Text Input Section -->
|
||||||
|
<div class="mb-8 p-4 border border-gray-200 rounded-md">
|
||||||
|
<h3 class="text-xl font-semibold mb-4">Text Hashing</h3>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="textInput" class="block text-sm font-medium text-gray-700 mb-1">Enter text to hash:</label>
|
||||||
|
<textarea
|
||||||
|
id="textInput"
|
||||||
|
bind:value={textInput}
|
||||||
|
class="w-full p-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
rows="3"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
on:click={hashText}
|
||||||
|
class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
||||||
|
>
|
||||||
|
Hash Text
|
||||||
|
</button>
|
||||||
|
{#if blakeHash}
|
||||||
|
<div class="mt-4">
|
||||||
|
<h4 class="text-sm font-medium text-gray-700 mb-1">Blake3 Hash:</h4>
|
||||||
|
<div class="p-2 bg-gray-100 rounded-md overflow-x-auto">
|
||||||
|
<code class="text-sm break-all">{blakeHash}</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- File Processing Section -->
|
||||||
|
<div class="p-4 border border-gray-200 rounded-md">
|
||||||
|
<h3 class="text-xl font-semibold mb-4">File Processing</h3>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="fileInput" class="block text-sm font-medium text-gray-700 mb-1">Select a file to process:</label>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="fileInput"
|
||||||
|
on:change={handleFileSelect}
|
||||||
|
class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
on:click={processFile}
|
||||||
|
disabled={!selectedFile || isProcessing}
|
||||||
|
class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:bg-blue-300 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
{isProcessing ? 'Processing...' : 'Process File'}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{#if selectedFile && (normalizedFilename || fileBlakeHash || ipfsCid || combinedKey)}
|
||||||
|
<div class="mt-4 space-y-3">
|
||||||
|
{#if normalizedFilename}
|
||||||
|
<div>
|
||||||
|
<h4 class="text-sm font-medium text-gray-700 mb-1">Normalized Filename:</h4>
|
||||||
|
<div class="p-2 bg-gray-100 rounded-md overflow-x-auto">
|
||||||
|
<code class="text-sm break-all">{normalizedFilename}</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if fileBlakeHash}
|
||||||
|
<div>
|
||||||
|
<h4 class="text-sm font-medium text-gray-700 mb-1">Blake3 Hash:</h4>
|
||||||
|
<div class="p-2 bg-gray-100 rounded-md overflow-x-auto">
|
||||||
|
<code class="text-sm break-all">{fileBlakeHash}</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if ipfsCid}
|
||||||
|
<div>
|
||||||
|
<h4 class="text-sm font-medium text-gray-700 mb-1">IPFS CID:</h4>
|
||||||
|
<div class="p-2 bg-gray-100 rounded-md overflow-x-auto">
|
||||||
|
<code class="text-sm break-all">{ipfsCid}</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if combinedKey}
|
||||||
|
<div>
|
||||||
|
<h4 class="text-sm font-medium text-gray-700 mb-1">Combined Key (Blake Hash + IPFS CID):</h4>
|
||||||
|
<div class="p-2 bg-gray-100 rounded-md overflow-x-auto">
|
||||||
|
<code class="text-sm break-all">{combinedKey}</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Messages -->
|
||||||
|
{#if errorMessage}
|
||||||
|
<div class="mt-4 p-3 bg-red-100 text-red-700 rounded-md">
|
||||||
|
{errorMessage}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if successMessage}
|
||||||
|
<div class="mt-4 p-3 bg-green-100 text-green-700 rounded-md">
|
||||||
|
{successMessage}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
@ -1,15 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import MarkdownContent from "./MarkdownContent.svelte";
|
import MarkdownContent from "./MarkdownContent.svelte";
|
||||||
import IPFSDemo from "./IPFSDemo.svelte";
|
|
||||||
import Button from "$lib/components/ui/button/button.svelte";
|
|
||||||
|
|
||||||
export let contentPath: string = "introduction/introduction";
|
export let contentPath: string = "introduction/introduction";
|
||||||
|
|
||||||
// Default to introduction if no path is provided
|
// Default to introduction if no path is provided
|
||||||
$: actualPath = contentPath || "introduction/introduction";
|
$: actualPath = contentPath || "introduction/introduction";
|
||||||
|
|
||||||
// Flag to show IPFS demo
|
|
||||||
let showIPFSDemo = false;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="">
|
<div class="">
|
||||||
@ -20,35 +15,17 @@
|
|||||||
<MarkdownContent path={actualPath} />
|
<MarkdownContent path={actualPath} />
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- IPFS Demo Section -->
|
<div class="p-8 text-center">
|
||||||
<section class="mb-12 sm:mb-16 md:mb-20">
|
<h2 class="text-2xl sm:text-3xl font-bold mb-4 text-text">
|
||||||
<div class="text-center mb-8 sm:mb-12">
|
Welcome to SecureWeb
|
||||||
<h2
|
|
||||||
class="text-2xl sm:text-3xl font-bold mb-3 sm:mb-4 text-text"
|
|
||||||
>
|
|
||||||
IPFS Integration
|
|
||||||
</h2>
|
</h2>
|
||||||
<p
|
<p class="text-lg text-text-secondary max-w-3xl mx-auto">
|
||||||
class="text-lg sm:text-xl text-text-secondary max-w-3xl mx-auto"
|
A decentralized web platform with IPFS integration and Blake
|
||||||
>
|
hashing for content security.
|
||||||
Experience the power of decentralized content storage and
|
</p>
|
||||||
retrieval with IPFS.
|
<p class="mt-4 text-text-secondary">
|
||||||
|
Use the sidebar navigation to explore the content and demos.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="mt-6">
|
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
size="lg"
|
|
||||||
on:click={() => (showIPFSDemo = !showIPFSDemo)}
|
|
||||||
>
|
|
||||||
{showIPFSDemo ? "Hide IPFS Demo" : "Show IPFS Demo"}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if showIPFSDemo}
|
|
||||||
<IPFSDemo />
|
|
||||||
{/if}
|
|
||||||
</section>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
import Footer from "./Footer.svelte";
|
import Footer from "./Footer.svelte";
|
||||||
import NavDataProvider from "./NavDataProvider.svelte";
|
import NavDataProvider from "./NavDataProvider.svelte";
|
||||||
import Home from "./Home.svelte";
|
import Home from "./Home.svelte";
|
||||||
|
import BlakeHashDemo from "./BlakeHashDemo.svelte";
|
||||||
|
import IPFSDemo from "./IPFSDemo.svelte";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import ThemeProvider from "../lib/theme/ThemeProvider.svelte";
|
import ThemeProvider from "../lib/theme/ThemeProvider.svelte";
|
||||||
|
|
||||||
@ -82,7 +84,15 @@
|
|||||||
sidebarVisible && !isMobile ? "md:ml-64" : "ml-0"
|
sidebarVisible && !isMobile ? "md:ml-64" : "ml-0"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{#if selectedContentPath}
|
{#if selectedContentPath === "demos/blake-hash-demo"}
|
||||||
|
<div class="p-4">
|
||||||
|
<BlakeHashDemo />
|
||||||
|
</div>
|
||||||
|
{:else if selectedContentPath === "demos/ipfs-demo"}
|
||||||
|
<div class="p-4">
|
||||||
|
<IPFSDemo />
|
||||||
|
</div>
|
||||||
|
{:else if selectedContentPath}
|
||||||
<Home contentPath={selectedContentPath} />
|
<Home contentPath={selectedContentPath} />
|
||||||
{:else}
|
{:else}
|
||||||
<slot />
|
<slot />
|
||||||
|
168
sweb/src/services/content-processor.service.ts
Normal file
168
sweb/src/services/content-processor.service.ts
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/**
|
||||||
|
* Content Processing Service
|
||||||
|
* Implements the content processing pipeline as described in the specs
|
||||||
|
* Uses Blake hashing for content integrity and encryption key generation
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { cryptoService } from './crypto.service';
|
||||||
|
import { ipfsService } from './ipfs.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for processed content metadata
|
||||||
|
*/
|
||||||
|
interface ProcessedContentMetadata {
|
||||||
|
originalFilename: string;
|
||||||
|
normalizedFilename: string;
|
||||||
|
blakeHash: string;
|
||||||
|
ipfsCid: string;
|
||||||
|
combinedKey: string;
|
||||||
|
contentType: string;
|
||||||
|
size: number;
|
||||||
|
timestamp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for processing content files
|
||||||
|
* Implements the content processing pipeline:
|
||||||
|
* 1. File discovery
|
||||||
|
* 2. Filename normalization
|
||||||
|
* 3. Original content hashing (Blake)
|
||||||
|
* 4. Content encryption
|
||||||
|
* 5. Encrypted content upload to IPFS
|
||||||
|
*/
|
||||||
|
class ContentProcessorService {
|
||||||
|
/**
|
||||||
|
* Process a file through the content processing pipeline
|
||||||
|
* @param file - File to process
|
||||||
|
* @returns Promise<ProcessedContentMetadata> - Metadata of the processed content
|
||||||
|
*/
|
||||||
|
async processFile(file: File): Promise<ProcessedContentMetadata> {
|
||||||
|
try {
|
||||||
|
// Step 1: Get file information
|
||||||
|
const originalFilename = file.name;
|
||||||
|
|
||||||
|
// Step 2: Normalize filename (lowercase and snake_case)
|
||||||
|
const normalizedFilename = this.normalizeFilename(originalFilename);
|
||||||
|
|
||||||
|
// Step 3: Hash the original content using Blake3
|
||||||
|
const blakeHash = await cryptoService.hashFileBlake3AsHex(file);
|
||||||
|
|
||||||
|
// Step 4: Encrypt the content using the Blake hash as the key
|
||||||
|
// Note: This is a placeholder for actual encryption implementation
|
||||||
|
const encryptedContent = await this.encryptContent(file, blakeHash);
|
||||||
|
|
||||||
|
// Step 5: Upload the encrypted content to IPFS
|
||||||
|
const ipfsCid = await ipfsService.uploadContent(encryptedContent);
|
||||||
|
|
||||||
|
// Create the combined key (Blake hash + IPFS CID)
|
||||||
|
const combinedKey = cryptoService.combineBlakeHashAndIpfsCid(blakeHash, ipfsCid);
|
||||||
|
|
||||||
|
// Return the metadata
|
||||||
|
return {
|
||||||
|
originalFilename,
|
||||||
|
normalizedFilename,
|
||||||
|
blakeHash,
|
||||||
|
ipfsCid,
|
||||||
|
combinedKey,
|
||||||
|
contentType: file.type,
|
||||||
|
size: file.size,
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error processing file:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a filename to lowercase and snake_case
|
||||||
|
* @param filename - Original filename
|
||||||
|
* @returns Normalized filename
|
||||||
|
*/
|
||||||
|
normalizeFilename(filename: string): string {
|
||||||
|
// Remove file extension
|
||||||
|
const parts = filename.split('.');
|
||||||
|
const extension = parts.pop() || '';
|
||||||
|
let name = parts.join('.');
|
||||||
|
|
||||||
|
// Convert to lowercase
|
||||||
|
name = name.toLowerCase();
|
||||||
|
|
||||||
|
// Replace spaces and special characters with underscores
|
||||||
|
name = name.replace(/[^a-z0-9]/g, '_');
|
||||||
|
|
||||||
|
// Replace multiple underscores with a single one
|
||||||
|
name = name.replace(/_+/g, '_');
|
||||||
|
|
||||||
|
// Remove leading and trailing underscores
|
||||||
|
name = name.replace(/^_+|_+$/g, '');
|
||||||
|
|
||||||
|
// Add extension back if it exists
|
||||||
|
if (extension) {
|
||||||
|
name = `${name}.${extension.toLowerCase()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt content using the Blake hash as the key
|
||||||
|
* This is a placeholder for actual encryption implementation
|
||||||
|
* @param file - File to encrypt
|
||||||
|
* @param key - Encryption key (Blake hash)
|
||||||
|
* @returns Promise<string> - Encrypted content as string
|
||||||
|
*/
|
||||||
|
async encryptContent(file: File, key: string): Promise<string> {
|
||||||
|
// TODO: Implement actual encryption
|
||||||
|
// For now, we'll just return a mock encrypted content
|
||||||
|
// This should be replaced with actual encryption using the Blake hash as the key
|
||||||
|
|
||||||
|
// Convert the file to a string
|
||||||
|
const arrayBuffer = await file.arrayBuffer();
|
||||||
|
const contentBytes = new Uint8Array(arrayBuffer);
|
||||||
|
|
||||||
|
// Mock encryption by XORing with the key bytes
|
||||||
|
// This is NOT secure and is only for demonstration purposes
|
||||||
|
const keyBytes = cryptoService.hexToBytes(key);
|
||||||
|
const encryptedBytes = new Uint8Array(contentBytes.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < contentBytes.length; i++) {
|
||||||
|
encryptedBytes[i] = contentBytes[i] ^ keyBytes[i % keyBytes.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to base64 for storage
|
||||||
|
return btoa(String.fromCharCode.apply(null, Array.from(encryptedBytes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt content using the Blake hash as the key
|
||||||
|
* This is a placeholder for actual decryption implementation
|
||||||
|
* @param encryptedContent - Encrypted content as string
|
||||||
|
* @param key - Decryption key (Blake hash)
|
||||||
|
* @returns Promise<Uint8Array> - Decrypted content as Uint8Array
|
||||||
|
*/
|
||||||
|
async decryptContent(encryptedContent: string, key: string): Promise<Uint8Array> {
|
||||||
|
// TODO: Implement actual decryption
|
||||||
|
// For now, we'll just return a mock decrypted content
|
||||||
|
// This should be replaced with actual decryption using the Blake hash as the key
|
||||||
|
|
||||||
|
// Convert the base64 string to bytes
|
||||||
|
const encryptedBytes = new Uint8Array(
|
||||||
|
atob(encryptedContent).split('').map(c => c.charCodeAt(0))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mock decryption by XORing with the key bytes
|
||||||
|
// This is NOT secure and is only for demonstration purposes
|
||||||
|
const keyBytes = cryptoService.hexToBytes(key);
|
||||||
|
const decryptedBytes = new Uint8Array(encryptedBytes.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < encryptedBytes.length; i++) {
|
||||||
|
decryptedBytes[i] = encryptedBytes[i] ^ keyBytes[i % keyBytes.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
return decryptedBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a singleton instance
|
||||||
|
export const contentProcessorService = new ContentProcessorService();
|
125
sweb/src/services/crypto.service.ts
Normal file
125
sweb/src/services/crypto.service.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/**
|
||||||
|
* Cryptography Service
|
||||||
|
* Provides cryptographic functions for content processing
|
||||||
|
* Implements Blake hashing algorithm for content integrity and encryption key generation
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { blake3 } from '@noble/hashes/blake3';
|
||||||
|
import { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for cryptographic operations
|
||||||
|
* Provides methods for hashing, encryption, and decryption
|
||||||
|
*/
|
||||||
|
class CryptoService {
|
||||||
|
/**
|
||||||
|
* Hash content using Blake3 algorithm
|
||||||
|
* @param content - Content to hash as Uint8Array
|
||||||
|
* @returns Blake3 hash as Uint8Array
|
||||||
|
*/
|
||||||
|
hashBlake3(content: Uint8Array): Uint8Array {
|
||||||
|
return blake3(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash content using Blake3 algorithm with custom output length
|
||||||
|
* @param content - Content to hash as Uint8Array
|
||||||
|
* @param outputLength - Length of the output hash in bytes
|
||||||
|
* @returns Blake3 hash as Uint8Array
|
||||||
|
*/
|
||||||
|
hashBlake3WithLength(content: Uint8Array, outputLength: number): Uint8Array {
|
||||||
|
return blake3(content, { dkLen: outputLength });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash string content using Blake3 algorithm
|
||||||
|
* @param content - Content to hash as string
|
||||||
|
* @returns Blake3 hash as Uint8Array
|
||||||
|
*/
|
||||||
|
hashStringBlake3(content: string): Uint8Array {
|
||||||
|
const contentBytes = utf8ToBytes(content);
|
||||||
|
return this.hashBlake3(contentBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash string content using Blake3 algorithm and return as hex string
|
||||||
|
* @param content - Content to hash as string
|
||||||
|
* @returns Blake3 hash as hex string
|
||||||
|
*/
|
||||||
|
hashStringBlake3AsHex(content: string): string {
|
||||||
|
const hash = this.hashStringBlake3(content);
|
||||||
|
return bytesToHex(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash file content using Blake3 algorithm
|
||||||
|
* @param file - File to hash
|
||||||
|
* @returns Promise<Uint8Array> - Blake3 hash as Uint8Array
|
||||||
|
*/
|
||||||
|
async hashFileBlake3(file: File): Promise<Uint8Array> {
|
||||||
|
const buffer = await file.arrayBuffer();
|
||||||
|
const contentBytes = new Uint8Array(buffer);
|
||||||
|
return this.hashBlake3(contentBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash file content using Blake3 algorithm and return as hex string
|
||||||
|
* @param file - File to hash
|
||||||
|
* @returns Promise<string> - Blake3 hash as hex string
|
||||||
|
*/
|
||||||
|
async hashFileBlake3AsHex(file: File): Promise<string> {
|
||||||
|
const hash = await this.hashFileBlake3(file);
|
||||||
|
return bytesToHex(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combine Blake hash and IPFS CID into a single key string
|
||||||
|
* Format: <blake_hash>:<ipfs_cid>
|
||||||
|
* @param blakeHash - Blake hash as hex string
|
||||||
|
* @param ipfsCid - IPFS CID as string
|
||||||
|
* @returns Combined key string
|
||||||
|
*/
|
||||||
|
combineBlakeHashAndIpfsCid(blakeHash: string, ipfsCid: string): string {
|
||||||
|
return `${blakeHash}:${ipfsCid}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split combined key string into Blake hash and IPFS CID
|
||||||
|
* @param combinedKey - Combined key string in format <blake_hash>:<ipfs_cid>
|
||||||
|
* @returns Object with blakeHash and ipfsCid
|
||||||
|
*/
|
||||||
|
splitCombinedKey(combinedKey: string): { blakeHash: string; ipfsCid: string } {
|
||||||
|
const [blakeHash, ipfsCid] = combinedKey.split(':');
|
||||||
|
return { blakeHash, ipfsCid };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert hex string to Uint8Array
|
||||||
|
* @param hex - Hex string
|
||||||
|
* @returns Uint8Array
|
||||||
|
*/
|
||||||
|
hexToBytes(hex: string): Uint8Array {
|
||||||
|
return hexToBytes(hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert Uint8Array to hex string
|
||||||
|
* @param bytes - Uint8Array
|
||||||
|
* @returns Hex string
|
||||||
|
*/
|
||||||
|
bytesToHex(bytes: Uint8Array): string {
|
||||||
|
return bytesToHex(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert string to Uint8Array using UTF-8 encoding
|
||||||
|
* @param str - String to convert
|
||||||
|
* @returns Uint8Array
|
||||||
|
*/
|
||||||
|
stringToBytes(str: string): Uint8Array {
|
||||||
|
return utf8ToBytes(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a singleton instance
|
||||||
|
export const cryptoService = new CryptoService();
|
Loading…
Reference in New Issue
Block a user