Skip to content

Commit b6c9441

Browse files
author
teycir
committed
feat: remove Turnstile CAPTCHA from seal creation API
- Removed Turnstile token validation and import from create-seal route for simplified user experience - Added debug logging to encryption process and API response handling - Refactored vault page to handle async params with useState/useEffect - Updated unlock date input to use proper label and tooltip structure
1 parent 1664ae5 commit b6c9441

5 files changed

Lines changed: 57 additions & 46 deletions

File tree

app/api/create-seal/route.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
} from "@/lib/validation";
1111
import { ErrorCode, createErrorResponse } from "@/lib/errors";
1212
import { validateHTTPMethod, validateOrigin } from "@/lib/security";
13-
import { validateTurnstile } from "@/lib/turnstile";
1413

1514
export async function POST(request: NextRequest) {
1615
if (!validateHTTPMethod(request, ["POST"])) {
@@ -31,7 +30,6 @@ export async function POST(request: NextRequest) {
3130
return createAPIRoute(
3231
async ({ container, request: ctx, ip }) => {
3332
const formData = await ctx.formData();
34-
const turnstileToken = formData.get("cf-turnstile-response") as string;
3533
const encryptedBlob = formData.get("encryptedBlob") as File;
3634
const keyB = formData.get("keyB") as string;
3735
const iv = formData.get("iv") as string;
@@ -41,15 +39,6 @@ export async function POST(request: NextRequest) {
4139
? parseInt(formData.get("pulseInterval") as string)
4240
: undefined;
4341

44-
if (!turnstileToken) {
45-
return createErrorResponse(ErrorCode.INVALID_INPUT, "Turnstile token required");
46-
}
47-
48-
const turnstileValid = await validateTurnstile(turnstileToken, ip);
49-
if (!turnstileValid) {
50-
return createErrorResponse(ErrorCode.INVALID_INPUT, "Turnstile validation failed");
51-
}
52-
5342
if (!encryptedBlob || !keyB || !iv || !unlockTime || isNaN(unlockTime)) {
5443
return createErrorResponse(
5544
ErrorCode.INVALID_UNLOCK_TIME,

app/page.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ export default function HomePage() {
372372

373373
const data = await response.json() as { success: boolean; publicUrl: string; pulseToken?: string; receipt?: any; error?: string };
374374

375+
console.log('[CREATE-SEAL] Response:', response.status, data);
376+
375377
if (data.success) {
376378
setEncryptionProgress(95);
377379
const origin = globalThis.window ? globalThis.window.location.origin : '';
@@ -724,11 +726,12 @@ export default function HomePage() {
724726
exit={{ opacity: 0, height: 0 }}
725727
className="relative z-50"
726728
>
727-
<div className="block text-sm mb-2 text-neon-green/80 font-bold tooltip">
729+
<label htmlFor="unlock-date" className="block text-sm mb-2 text-neon-green/80 font-bold">
728730
UNLOCK DATE & TIME
729-
<span className="tooltip-text">Select when the seal will automatically unlock. Must be at least 1 minute in the future.</span>
730-
</div>
731+
</label>
732+
<p className="text-xs text-neon-green/50 mb-2">Select when the seal will automatically unlock. Must be at least 1 minute in the future.</p>
731733
<input
734+
id="unlock-date"
732735
type="datetime-local"
733736
value={unlockDate}
734737
onChange={(e) => setUnlockDate(e.target.value)}

app/v/[id]/page.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@ interface SealStatus {
2020
}
2121

2222
export default function VaultPage({ params }: { params: Promise<{ id: string }> }) {
23-
return <VaultPageWrapper params={params} />;
24-
}
23+
const [id, setId] = useState<string | null>(null);
24+
25+
useEffect(() => {
26+
params.then(p => setId(p.id));
27+
}, [params]);
28+
29+
if (!id) return null;
2530

26-
async function VaultPageWrapper({ params }: { params: Promise<{ id: string }> }) {
27-
const { id } = await params;
2831
return <VaultPageClient id={id} />;
2932
}
3033

lib/crypto.ts

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export async function encryptData(data: string | File): Promise<EncryptionResult
8585

8686
// Generate random IV
8787
const iv = crypto.getRandomValues(new Uint8Array(12));
88+
console.log('[ENCRYPT] IV length:', iv.length, 'bytes');
8889

8990
// Encrypt with master key
9091
const encryptedBlob = await crypto.subtle.encrypt(
@@ -96,12 +97,14 @@ export async function encryptData(data: string | File): Promise<EncryptionResult
9697
// Export keys as base64 strings
9798
const keyABuffer = await crypto.subtle.exportKey('raw', keyA);
9899
const keyBBuffer = await crypto.subtle.exportKey('raw', keyB);
100+
const ivBase64 = arrayBufferToBase64(iv.buffer);
101+
console.log('[ENCRYPT] IV base64 length:', ivBase64.length, 'value:', ivBase64);
99102

100103
return {
101104
encryptedBlob,
102105
keyA: arrayBufferToBase64(keyABuffer),
103106
keyB: arrayBufferToBase64(keyBBuffer),
104-
iv: arrayBufferToBase64(iv.buffer),
107+
iv: ivBase64,
105108
};
106109
}
107110

@@ -110,33 +113,43 @@ export async function decryptData(
110113
encryptedBlob: ArrayBuffer,
111114
keys: DecryptionKeys
112115
): Promise<ArrayBuffer> {
113-
// Import keys from base64
114-
const keyA = await crypto.subtle.importKey(
115-
'raw',
116-
base64ToArrayBuffer(keys.keyA),
117-
{ name: 'AES-GCM' },
118-
true,
119-
['decrypt']
120-
);
121-
122-
const keyB = await crypto.subtle.importKey(
123-
'raw',
124-
base64ToArrayBuffer(keys.keyB),
125-
{ name: 'AES-GCM' },
126-
true,
127-
['decrypt']
128-
);
116+
try {
117+
// Import keys from base64
118+
const keyA = await crypto.subtle.importKey(
119+
'raw',
120+
base64ToArrayBuffer(keys.keyA),
121+
{ name: 'AES-GCM' },
122+
true,
123+
['decrypt']
124+
);
125+
126+
const keyB = await crypto.subtle.importKey(
127+
'raw',
128+
base64ToArrayBuffer(keys.keyB),
129+
{ name: 'AES-GCM' },
130+
true,
131+
['decrypt']
132+
);
129133

130-
// Derive master key
131-
const masterKey = await deriveMasterKey(keyA, keyB);
132-
133-
// Decrypt
134-
const iv = base64ToArrayBuffer(keys.iv);
135-
return await crypto.subtle.decrypt(
136-
{ name: 'AES-GCM', iv },
137-
masterKey,
138-
encryptedBlob
139-
) as ArrayBuffer;
134+
// Derive master key
135+
const masterKey = await deriveMasterKey(keyA, keyB);
136+
137+
// Decrypt
138+
console.log('[DECRYPT] IV base64 received:', keys.iv, 'length:', keys.iv.length);
139+
const iv = base64ToArrayBuffer(keys.iv);
140+
console.log('[DECRYPT] IV ArrayBuffer byteLength:', iv.byteLength);
141+
return await crypto.subtle.decrypt(
142+
{ name: 'AES-GCM', iv },
143+
masterKey,
144+
encryptedBlob
145+
) as ArrayBuffer;
146+
} catch (error) {
147+
// Check if it's an IV length error
148+
if (error instanceof Error && error.message.includes('iv')) {
149+
throw new Error('This seal was created with an incompatible version. Please create a new seal.');
150+
}
151+
throw error;
152+
}
140153
}
141154

142155
// Utility functions

lib/validation.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ export function validateKey(key: string, name: string): ValidationResult {
3131
if (!/^[A-Za-z0-9+/=]+$/.test(key)) {
3232
return { valid: false, error: `${name} must be base64 encoded` };
3333
}
34-
if (key.length < 32 || key.length > 100) {
34+
// IV is 12 bytes = 16 chars base64, Keys are 32 bytes = 44 chars base64
35+
const minLength = name === 'IV' ? 16 : 32;
36+
const maxLength = name === 'IV' ? 16 : 100;
37+
if (key.length < minLength || key.length > maxLength) {
3538
return { valid: false, error: `${name} has invalid length` };
3639
}
3740
return { valid: true };

0 commit comments

Comments
 (0)