1
API.md
1
API.md
@@ -179,7 +179,6 @@ The API automatically detects hash types based on length and format:
|
||||
| SHA1 | 40 | `^[a-f0-9]{40}$` |
|
||||
| SHA256 | 64 | `^[a-f0-9]{64}$` |
|
||||
| SHA512 | 128 | `^[a-f0-9]{128}$` |
|
||||
| Bcrypt | 60 | `^\$2[abxy]\$` |
|
||||
|
||||
Hashes are case-insensitive.
|
||||
|
||||
|
||||
@@ -10,12 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
|
||||
#### Core Features
|
||||
- Hash search functionality for MD5, SHA1, SHA256, SHA512, and Bcrypt
|
||||
- Hash search functionality for MD5, SHA1, SHA256, and SHA512
|
||||
- Hash generation from plaintext input
|
||||
- Automatic detection of hash types based on length and pattern
|
||||
- Real-time hash generation with instant results
|
||||
- Copy to clipboard functionality for all hash values
|
||||
- Bcrypt verification support
|
||||
|
||||
#### Backend
|
||||
- Elasticsearch integration with configurable endpoint
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
## ✨ Key Features
|
||||
|
||||
### 🔍 Hash Search
|
||||
- Search for MD5, SHA1, SHA256, SHA512, and Bcrypt hashes
|
||||
- Search for MD5, SHA1, SHA256, and SHA512 hashes
|
||||
- Automatic hash type detection
|
||||
- Case-insensitive matching
|
||||
- Real-time results
|
||||
@@ -174,7 +174,6 @@ export ELASTICSEARCH_NODE=http://localhost:9200
|
||||
| SHA1 | 40 | `^[a-f0-9]{40}$` |
|
||||
| SHA256 | 64 | `^[a-f0-9]{64}$` |
|
||||
| SHA512 | 128 | `^[a-f0-9]{128}$` |
|
||||
| Bcrypt | 60 | `^\$2[abxy]\$` |
|
||||
|
||||
---
|
||||
|
||||
@@ -245,7 +244,6 @@ export ELASTICSEARCH_NODE=http://localhost:9200
|
||||
## 📈 Future Enhancements
|
||||
|
||||
### Planned Features
|
||||
- Bcrypt hash validation
|
||||
- Argon2 hash support
|
||||
- Search history
|
||||
- Batch lookup
|
||||
|
||||
@@ -25,7 +25,6 @@ npm run index-file -- --help # Show help
|
||||
| SHA1 | 40 | `5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8` |
|
||||
| SHA256 | 64 | `5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8` |
|
||||
| SHA512 | 128 | `b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb9...` |
|
||||
| Bcrypt | 60 | `$2b$10$N9qo8uLOickgx2ZMRZoMye...` |
|
||||
|
||||
## 🔌 API Quick Reference
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ A modern, high-performance hash search and generation tool powered by Elasticsea
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- 🔍 **Hash Lookup**: Search for MD5, SHA1, SHA256, SHA512, and Bcrypt hashes
|
||||
- 🔍 **Hash Lookup**: Search for MD5, SHA1, SHA256, and SHA512 hashes
|
||||
- 🔑 **Hash Generation**: Generate multiple hash types from plaintext
|
||||
- 💾 **Auto-Indexing**: Automatically stores searched plaintext and hashes
|
||||
- 📊 **Elasticsearch Backend**: Scalable storage with 10 shards for performance
|
||||
@@ -274,7 +274,6 @@ npm run lint
|
||||
| SHA1 | 40 | `^[a-f0-9]{40}$` |
|
||||
| SHA256 | 64 | `^[a-f0-9]{64}$` |
|
||||
| SHA512 | 128 | `^[a-f0-9]{128}$` |
|
||||
| Bcrypt | 60 | `^\$2[abxy]\$` |
|
||||
|
||||
## 🚀 Performance
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ interface HashDocument {
|
||||
sha1: string;
|
||||
sha256: string;
|
||||
sha512: string;
|
||||
bcrypt: string;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
@@ -44,7 +43,7 @@ export async function POST(request: NextRequest) {
|
||||
index: INDEX_NAME,
|
||||
query: {
|
||||
term: {
|
||||
[hashType]: hashType === 'bcrypt' ? cleanQuery : cleanQueryLower
|
||||
[hashType]: cleanQueryLower
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -66,7 +65,6 @@ export async function POST(request: NextRequest) {
|
||||
sha1: source.sha1,
|
||||
sha256: source.sha256,
|
||||
sha512: source.sha512,
|
||||
bcrypt: source.bcrypt,
|
||||
}
|
||||
};
|
||||
})
|
||||
@@ -101,11 +99,10 @@ export async function POST(request: NextRequest) {
|
||||
sha1: existingDoc.sha1,
|
||||
sha256: existingDoc.sha256,
|
||||
sha512: existingDoc.sha512,
|
||||
bcrypt: existingDoc.bcrypt,
|
||||
};
|
||||
} else {
|
||||
// Plaintext not found, generate hashes and check if any hash already exists
|
||||
hashes = await generateHashes(cleanQuery);
|
||||
hashes = generateHashes(cleanQuery);
|
||||
|
||||
const hashExistsResponse = await esClient.search<HashDocument>({
|
||||
index: INDEX_NAME,
|
||||
@@ -147,7 +144,6 @@ export async function POST(request: NextRequest) {
|
||||
sha1: hashes.sha1,
|
||||
sha256: hashes.sha256,
|
||||
sha512: hashes.sha512,
|
||||
bcrypt: hashes.bcrypt,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ const geistMono = Geist_Mono({
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Hasher - Hash Search & Generator",
|
||||
description: "Search for hashes or generate them from plaintext. Supports MD5, SHA1, SHA256, SHA512, and Bcrypt. Powered by Elasticsearch.",
|
||||
keywords: ["hash", "md5", "sha1", "sha256", "sha512", "bcrypt", "hash generator", "hash search", "elasticsearch"],
|
||||
description: "Search for hashes or generate them from plaintext. Supports MD5, SHA1, SHA256, and SHA512. Powered by Elasticsearch.",
|
||||
keywords: ["hash", "md5", "sha1", "sha256", "sha512", "hash generator", "hash search", "elasticsearch"],
|
||||
authors: [{ name: "Hasher" }],
|
||||
creator: "Hasher",
|
||||
publisher: "Hasher",
|
||||
@@ -28,7 +28,7 @@ export const metadata: Metadata = {
|
||||
openGraph: {
|
||||
type: "website",
|
||||
title: "Hasher - Hash Search & Generator",
|
||||
description: "Search for hashes or generate them from plaintext. Supports MD5, SHA1, SHA256, SHA512, and Bcrypt.",
|
||||
description: "Search for hashes or generate them from plaintext. Supports MD5, SHA1, SHA256, and SHA512.",
|
||||
siteName: "Hasher",
|
||||
images: [
|
||||
{
|
||||
@@ -42,7 +42,7 @@ export const metadata: Metadata = {
|
||||
twitter: {
|
||||
card: "summary",
|
||||
title: "Hasher - Hash Search & Generator",
|
||||
description: "Search for hashes or generate them from plaintext. Supports MD5, SHA1, SHA256, SHA512, and Bcrypt.",
|
||||
description: "Search for hashes or generate them from plaintext. Supports MD5, SHA1, SHA256, and SHA512.",
|
||||
images: ["/logo.png"],
|
||||
},
|
||||
viewport: {
|
||||
|
||||
@@ -15,7 +15,6 @@ interface SearchResult {
|
||||
sha1: string;
|
||||
sha256: string;
|
||||
sha512: string;
|
||||
bcrypt: string;
|
||||
};
|
||||
results?: Array<{
|
||||
plaintext: string;
|
||||
@@ -24,7 +23,6 @@ interface SearchResult {
|
||||
sha1: string;
|
||||
sha256: string;
|
||||
sha512: string;
|
||||
bcrypt: string;
|
||||
};
|
||||
}>;
|
||||
message?: string;
|
||||
@@ -144,7 +142,7 @@ export default function Home() {
|
||||
Search for hashes or generate them from plaintext
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 mt-2">
|
||||
Supports MD5, SHA1, SHA256, SHA512, and Bcrypt
|
||||
Supports MD5, SHA1, SHA256, and SHA512
|
||||
</p>
|
||||
{stats && (
|
||||
<div className="flex items-center justify-center gap-4 mt-4 text-sm text-gray-500">
|
||||
@@ -214,7 +212,6 @@ export default function Home() {
|
||||
<HashDisplay label="SHA1" value={result.hashes!.sha1} field="sha1-gen" />
|
||||
<HashDisplay label="SHA256" value={result.hashes!.sha256} field="sha256-gen" />
|
||||
<HashDisplay label="SHA512" value={result.hashes!.sha512} field="sha512-gen" />
|
||||
<HashDisplay label="Bcrypt" value={result.hashes!.bcrypt} field="bcrypt-gen" />
|
||||
</div>
|
||||
{result.wasGenerated && (
|
||||
<div className="mt-6 bg-blue-50 border border-blue-200 rounded-xl p-4">
|
||||
@@ -260,7 +257,6 @@ export default function Home() {
|
||||
<HashDisplay label="SHA1" value={item.hashes.sha1} field={`sha1-${idx}`} />
|
||||
<HashDisplay label="SHA256" value={item.hashes.sha256} field={`sha256-${idx}`} />
|
||||
<HashDisplay label="SHA512" value={item.hashes.sha512} field={`sha512-${idx}`} />
|
||||
<HashDisplay label="Bcrypt" value={item.hashes.bcrypt} field={`bcrypt-${idx}`} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -304,7 +300,7 @@ export default function Home() {
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-2">Generate Hashes</h3>
|
||||
<p className="text-gray-600">
|
||||
Enter any plaintext to instantly generate MD5, SHA1, SHA256, SHA512, and Bcrypt hashes. Results are saved automatically.
|
||||
Enter any plaintext to instantly generate MD5, SHA1, SHA256, and SHA512 hashes. Results are saved automatically.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -46,9 +46,6 @@ export const INDEX_MAPPING = {
|
||||
sha512: {
|
||||
type: 'keyword' as const
|
||||
},
|
||||
bcrypt: {
|
||||
type: 'keyword' as const
|
||||
},
|
||||
created_at: {
|
||||
type: 'date' as const
|
||||
}
|
||||
|
||||
23
lib/hash.ts
23
lib/hash.ts
@@ -1,5 +1,4 @@
|
||||
import crypto from 'crypto';
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
export interface HashResult {
|
||||
plaintext: string;
|
||||
@@ -7,22 +6,18 @@ export interface HashResult {
|
||||
sha1: string;
|
||||
sha256: string;
|
||||
sha512: string;
|
||||
bcrypt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all common hashes for a given plaintext
|
||||
*/
|
||||
export async function generateHashes(plaintext: string): Promise<HashResult> {
|
||||
const bcryptHash = await bcrypt.hash(plaintext, 10);
|
||||
|
||||
export function generateHashes(plaintext: string): HashResult {
|
||||
return {
|
||||
plaintext,
|
||||
md5: crypto.createHash('md5').update(plaintext).digest('hex'),
|
||||
sha1: crypto.createHash('sha1').update(plaintext).digest('hex'),
|
||||
sha256: crypto.createHash('sha256').update(plaintext).digest('hex'),
|
||||
sha512: crypto.createHash('sha512').update(plaintext).digest('hex'),
|
||||
bcrypt: bcryptHash,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,11 +47,6 @@ export function detectHashType(hash: string): string | null {
|
||||
return 'sha512';
|
||||
}
|
||||
|
||||
// BCrypt: starts with $2a$, $2b$, $2x$, or $2y$
|
||||
if (/^\$2[abxy]\$/.test(cleanHash)) {
|
||||
return 'bcrypt';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -66,14 +56,3 @@ export function detectHashType(hash: string): string | null {
|
||||
export function isHash(input: string): boolean {
|
||||
return detectHashType(input) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a plaintext against a bcrypt hash
|
||||
*/
|
||||
export async function verifyBcrypt(plaintext: string, hash: string): Promise<boolean> {
|
||||
try {
|
||||
return await bcrypt.compare(plaintext, hash);
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@elastic/elasticsearch": "^9.2.0",
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
"bcrypt": "^6.0.0",
|
||||
"lucide-react": "^0.555.0",
|
||||
"next": "15.4.8",
|
||||
"react": "19.1.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Hasher - Hash Search & Generator",
|
||||
"short_name": "Hasher",
|
||||
"description": "Search for hashes or generate them from plaintext. Supports MD5, SHA1, SHA256, SHA512, and Bcrypt.",
|
||||
"description": "Search for hashes or generate them from plaintext. Supports MD5, SHA1, SHA256, and SHA512.",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
|
||||
@@ -35,7 +35,6 @@ interface HashDocument {
|
||||
sha1: string;
|
||||
sha256: string;
|
||||
sha512: string;
|
||||
bcrypt: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
@@ -157,17 +156,13 @@ function deleteState(stateFile: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
async function generateHashes(plaintext: string): Promise<HashDocument> {
|
||||
const bcrypt = await import('bcrypt');
|
||||
const bcryptHash = await bcrypt.default.hash(plaintext, 10);
|
||||
|
||||
function generateHashes(plaintext: string): HashDocument {
|
||||
return {
|
||||
plaintext,
|
||||
md5: crypto.createHash('md5').update(plaintext).digest('hex'),
|
||||
sha1: crypto.createHash('sha1').update(plaintext).digest('hex'),
|
||||
sha256: crypto.createHash('sha256').update(plaintext).digest('hex'),
|
||||
sha512: crypto.createHash('sha512').update(plaintext).digest('hex'),
|
||||
bcrypt: bcryptHash,
|
||||
created_at: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
@@ -313,12 +308,10 @@ async function indexFile(filePath: string, batchSize: number, shouldResume: bool
|
||||
const bulkOperations: any[] = [];
|
||||
|
||||
// Generate hashes for all items in batch first
|
||||
const batchWithHashes = await Promise.all(
|
||||
batch.map(async (plaintext: string) => ({
|
||||
plaintext,
|
||||
hashes: await generateHashes(plaintext)
|
||||
}))
|
||||
);
|
||||
const batchWithHashes = batch.map((plaintext: string) => ({
|
||||
plaintext,
|
||||
hashes: generateHashes(plaintext)
|
||||
}));
|
||||
|
||||
if (checkDuplicates) {
|
||||
// Check which items already exist (by plaintext or any hash)
|
||||
|
||||
Referencia en una nueva incidencia
Block a user