Implement comprehensive SRP Buffer polyfill to fix verification errors

This commit is contained in:
Andreas Düren 2025-03-20 14:40:50 +01:00
parent 6289577898
commit 7f7ae4e8bf

384
start.sh
View File

@ -679,8 +679,10 @@ chmod 644 /app/data/web/runtime-config.js
# Create a custom URL patch file to fix the URL constructor error # Create a custom URL patch file to fix the URL constructor error
echo "==> Creating URL and SRP patch file" echo "==> Creating URL and SRP patch file"
cat << EOF > /app/data/web/ente-patches.js cat > /app/data/web/photos/static/ente-patches.js << 'ENDPATCHES'
(function() { (function() {
console.log('Applying Ente URL and SRP patches...');
// Save original URL constructor // Save original URL constructor
const originalURL = window.URL; const originalURL = window.URL;
@ -710,102 +712,364 @@ cat << EOF > /app/data/web/ente-patches.js
} }
}; };
// More robust Buffer implementation for SRP // Comprehensive Buffer polyfill for SRP
window.Buffer = window.Buffer || { const originalBuffer = window.Buffer;
window.Buffer = {
from: function(data, encoding) { from: function(data, encoding) {
// Handle undefined data - critical fix // Debug logging for the SRP calls
if (data === undefined) { console.debug('Buffer.from called with:',
console.warn('Buffer.from called with undefined data, creating empty buffer'); typeof data,
return { data === undefined ? 'undefined' :
data: '', data === null ? 'null' :
Array.isArray(data) ? 'array[' + data.length + ']' :
'value',
'encoding:', encoding);
// Handle undefined/null data - critical fix
if (data === undefined || data === null) {
console.warn('Buffer.from called with ' + (data === undefined ? 'undefined' : 'null') + ' data, creating empty buffer');
const result = {
data: new Uint8Array(0),
length: 0, length: 0,
toString: function() { return ''; } toString: function(enc) { return ''; }
}; };
// Add additional methods that SRP might use
result.slice = function() { return Buffer.from([]); };
result.readUInt32BE = function() { return 0; };
result.writeUInt32BE = function() { return result; };
return result;
}
// Special case for hex strings - very important for SRP
if (typeof data === 'string' && encoding === 'hex') {
// Convert hex string to byte array
const bytes = [];
for (let i = 0; i < data.length; i += 2) {
if (data.length - i >= 2) {
bytes.push(parseInt(data.substr(i, 2), 16));
}
}
const result = {
data: new Uint8Array(bytes),
length: bytes.length,
toString: function(enc) {
if (enc === 'hex' || !enc) {
return data; // Return original hex string
}
return bytes.map(b => String.fromCharCode(b)).join('');
}
};
// Add methods needed by SRP
result.slice = function(start, end) {
const slicedData = bytes.slice(start, end);
return Buffer.from(slicedData.map(b => b.toString(16).padStart(2, '0')).join(''), 'hex');
};
result.readUInt32BE = function(offset = 0) {
let value = 0;
for (let i = 0; i < 4; i++) {
value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0);
}
return value;
};
result.writeUInt32BE = function(value, offset = 0) {
for (let i = 0; i < 4; i++) {
if (offset + i < bytes.length) {
bytes[offset + 3 - i] = value & 0xFF;
value >>>= 8;
}
}
return result;
};
return result;
} }
// Handle string data // Handle string data
if (typeof data === 'string') { if (typeof data === 'string') {
return { const bytes = Array.from(data).map(c => c.charCodeAt(0));
data: data, const result = {
length: data.length, data: new Uint8Array(bytes),
length: bytes.length,
toString: function(enc) { toString: function(enc) {
if (enc === 'hex' && encoding === 'hex') { if (enc === 'hex') {
// Return the original hex string return bytes.map(b => b.toString(16).padStart(2, '0')).join('');
return data;
} }
return data; return data;
} }
}; };
// Add SRP methods
result.slice = function(start, end) {
return Buffer.from(data.slice(start, end));
};
result.readUInt32BE = function(offset = 0) {
let value = 0;
for (let i = 0; i < 4; i++) {
value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0);
}
return value;
};
result.writeUInt32BE = function(value, offset = 0) {
for (let i = 0; i < 4; i++) {
if (offset + i < bytes.length) {
bytes[offset + 3 - i] = value & 0xFF;
value >>>= 8;
}
}
return result;
};
return result;
} }
// Handle array/buffer data // Handle array/buffer data
if (Array.isArray(data) || ArrayBuffer.isView(data)) { if (Array.isArray(data) || ArrayBuffer.isView(data) || (data instanceof ArrayBuffer)) {
return { const bytes = Array.isArray(data) ? data : new Uint8Array(data.buffer || data);
data: data, const result = {
length: data.length, data: new Uint8Array(bytes),
toString: function() { length: bytes.length,
return Array.from(data).map(b => String.fromCharCode(b)).join(''); toString: function(enc) {
if (enc === 'hex') {
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
}
return Array.from(bytes).map(b => String.fromCharCode(b)).join('');
} }
}; };
// Add SRP methods
result.slice = function(start, end) {
return Buffer.from(bytes.slice(start, end));
};
result.readUInt32BE = function(offset = 0) {
let value = 0;
for (let i = 0; i < 4; i++) {
value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0);
}
return value;
};
result.writeUInt32BE = function(value, offset = 0) {
for (let i = 0; i < 4; i++) {
if (offset + i < bytes.length) {
bytes[offset + 3 - i] = value & 0xFF;
value >>>= 8;
}
}
return result;
};
return result;
} }
// Default fallback // Handle object data (last resort)
return { if (typeof data === 'object') {
data: data || '', console.warn('Buffer.from called with object type', data);
length: (data && data.length) || 0, const result = {
toString: function() { return ''; } data: data,
length: data.length || 0,
toString: function() { return JSON.stringify(data); }
};
// Add SRP methods
result.slice = function() { return Buffer.from({}); };
result.readUInt32BE = function() { return 0; };
result.writeUInt32BE = function() { return result; };
return result;
}
// Default fallback for any other type
console.warn('Buffer.from called with unsupported type:', typeof data);
const result = {
data: new Uint8Array(0),
length: 0,
toString: function() { return ''; },
slice: function() { return Buffer.from([]); },
readUInt32BE: function() { return 0; },
writeUInt32BE: function() { return result; }
}; };
return result;
}, },
isBuffer: function(obj) { return obj && obj.data !== undefined; },
alloc: function(size) { isBuffer: function(obj) {
const arr = new Array(size).fill(0); return obj && (obj.data !== undefined || (originalBuffer && originalBuffer.isBuffer && originalBuffer.isBuffer(obj)));
return { },
data: arr,
alloc: function(size, fill = 0) {
const bytes = new Array(size).fill(fill);
const result = {
data: new Uint8Array(bytes),
length: size, length: size,
toString: function() { return ''; } toString: function(enc) {
}; if (enc === 'hex') {
}, return bytes.map(b => b.toString(16).padStart(2, '0')).join('');
concat: function(list) { }
// Simple implementation that handles our use case return bytes.map(b => String.fromCharCode(b)).join('');
return {
data: list.map(b => b.data).join(''),
length: list.reduce((acc, b) => acc + (b.length || 0), 0),
toString: function() {
return list.map(b => b.toString()).join('');
} }
}; };
// Add SRP methods
result.slice = function(start, end) {
return Buffer.from(bytes.slice(start, end));
};
result.readUInt32BE = function(offset = 0) {
let value = 0;
for (let i = 0; i < 4; i++) {
value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0);
}
return value;
};
result.writeUInt32BE = function(value, offset = 0) {
for (let i = 0; i < 4; i++) {
if (offset + i < bytes.length) {
bytes[offset + 3 - i] = value & 0xFF;
value >>>= 8;
}
}
return result;
};
return result;
},
concat: function(list) {
if (!Array.isArray(list) || list.length === 0) {
return Buffer.alloc(0);
}
// Combine all buffers into one
const totalLength = list.reduce((acc, buf) => acc + (buf ? (buf.length || 0) : 0), 0);
const combinedArray = new Uint8Array(totalLength);
let offset = 0;
for (const buf of list) {
if (buf && buf.data) {
const data = buf.data instanceof Uint8Array ? buf.data : new Uint8Array(buf.data);
combinedArray.set(data, offset);
offset += buf.length;
}
}
const result = {
data: combinedArray,
length: totalLength,
toString: function(enc) {
if (enc === 'hex') {
return Array.from(combinedArray).map(b => b.toString(16).padStart(2, '0')).join('');
}
return Array.from(combinedArray).map(b => String.fromCharCode(b)).join('');
}
};
// Add SRP methods
result.slice = function(start, end) {
const slicedData = combinedArray.slice(start, end);
return Buffer.from(slicedData);
};
result.readUInt32BE = function(offset = 0) {
let value = 0;
for (let i = 0; i < 4; i++) {
value = (value << 8) + (offset + i < combinedArray.length ? combinedArray[offset + i] : 0);
}
return value;
};
result.writeUInt32BE = function(value, offset = 0) {
for (let i = 0; i < 4; i++) {
if (offset + i < combinedArray.length) {
combinedArray[offset + 3 - i] = value & 0xFF;
value >>>= 8;
}
}
return result;
};
return result;
} }
}; };
// Add missing crypto methods that SRP might need // Add missing crypto methods that SRP might need
if (window.crypto && !window.crypto.randomBytes) { if (window.crypto) {
window.crypto.randomBytes = function(size) { if (!window.crypto.randomBytes) {
const array = new Uint8Array(size); window.crypto.randomBytes = function(size) {
window.crypto.getRandomValues(array); const array = new Uint8Array(size);
return { window.crypto.getRandomValues(array);
data: array, return Buffer.from(array);
length: size,
toString: function(encoding) {
if (encoding === 'hex') {
return Array.from(array).map(b => b.toString(16).padStart(2, '0')).join('');
}
return Array.from(array).map(b => String.fromCharCode(b)).join('');
}
}; };
}; }
// Add cryptographic hash functions if needed
if (!window.crypto.createHash) {
window.crypto.createHash = function(algorithm) {
return {
update: function(data) {
this.data = data;
return this;
},
digest: async function(encoding) {
// Use the SubtleCrypto API for actual hashing
const dataBuffer = typeof this.data === 'string' ?
new TextEncoder().encode(this.data) :
this.data;
let hashBuffer;
try {
if (algorithm === 'sha256') {
hashBuffer = await window.crypto.subtle.digest('SHA-256', dataBuffer);
} else if (algorithm === 'sha1') {
hashBuffer = await window.crypto.subtle.digest('SHA-1', dataBuffer);
} else {
console.error('Unsupported hash algorithm:', algorithm);
return Buffer.alloc(32); // Return empty buffer as fallback
}
const hashArray = Array.from(new Uint8Array(hashBuffer));
if (encoding === 'hex') {
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
return Buffer.from(hashArray);
} catch (e) {
console.error('Hash calculation failed:', e);
return Buffer.alloc(32); // Return empty buffer as fallback
}
}
};
};
}
} }
// Patch the SRP implementation for browser compatibility // Patch the SRP implementation for browser compatibility
if (window.process) { if (!window.process) {
// Add any missing process methods window.process = {
window.process.nextTick = window.process.nextTick || function(fn) { env: {
setTimeout(fn, 0); NODE_ENV: 'production'
}
}; };
} }
// Add any missing process methods
window.process.nextTick = window.process.nextTick || function(fn) {
setTimeout(fn, 0);
};
console.log('Ente URL and SRP patches applied successfully'); console.log('Ente URL and SRP patches applied successfully');
})(); })();
EOF ENDPATCHES
# Create the static HTML files with scripts pre-injected # Create the static HTML files with scripts pre-injected
for app_dir in photos accounts auth cast; do for app_dir in photos accounts auth cast; do