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
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() {
console.log('Applying Ente URL and SRP patches...');
// Save original URL constructor
const originalURL = window.URL;
@ -710,102 +712,364 @@ cat << EOF > /app/data/web/ente-patches.js
}
};
// More robust Buffer implementation for SRP
window.Buffer = window.Buffer || {
// Comprehensive Buffer polyfill for SRP
const originalBuffer = window.Buffer;
window.Buffer = {
from: function(data, encoding) {
// Handle undefined data - critical fix
if (data === undefined) {
console.warn('Buffer.from called with undefined data, creating empty buffer');
return {
data: '',
// Debug logging for the SRP calls
console.debug('Buffer.from called with:',
typeof data,
data === undefined ? 'undefined' :
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,
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
if (typeof data === 'string') {
return {
data: data,
length: data.length,
const bytes = Array.from(data).map(c => c.charCodeAt(0));
const result = {
data: new Uint8Array(bytes),
length: bytes.length,
toString: function(enc) {
if (enc === 'hex' && encoding === 'hex') {
// Return the original hex string
return data;
if (enc === 'hex') {
return bytes.map(b => b.toString(16).padStart(2, '0')).join('');
}
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
if (Array.isArray(data) || ArrayBuffer.isView(data)) {
return {
data: data,
length: data.length,
toString: function() {
return Array.from(data).map(b => String.fromCharCode(b)).join('');
if (Array.isArray(data) || ArrayBuffer.isView(data) || (data instanceof ArrayBuffer)) {
const bytes = Array.isArray(data) ? data : new Uint8Array(data.buffer || data);
const result = {
data: new Uint8Array(bytes),
length: bytes.length,
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
return {
data: data || '',
length: (data && data.length) || 0,
toString: function() { return ''; }
// Handle object data (last resort)
if (typeof data === 'object') {
console.warn('Buffer.from called with object type', data);
const result = {
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) {
const arr = new Array(size).fill(0);
return {
data: arr,
isBuffer: function(obj) {
return obj && (obj.data !== undefined || (originalBuffer && originalBuffer.isBuffer && originalBuffer.isBuffer(obj)));
},
alloc: function(size, fill = 0) {
const bytes = new Array(size).fill(fill);
const result = {
data: new Uint8Array(bytes),
length: size,
toString: function() { return ''; }
};
},
concat: function(list) {
// Simple implementation that handles our use case
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('');
toString: function(enc) {
if (enc === 'hex') {
return bytes.map(b => b.toString(16).padStart(2, '0')).join('');
}
return 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;
},
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
if (window.crypto && !window.crypto.randomBytes) {
window.crypto.randomBytes = function(size) {
const array = new Uint8Array(size);
window.crypto.getRandomValues(array);
return {
data: 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('');
}
if (window.crypto) {
if (!window.crypto.randomBytes) {
window.crypto.randomBytes = function(size) {
const array = new Uint8Array(size);
window.crypto.getRandomValues(array);
return Buffer.from(array);
};
};
}
// 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
if (window.process) {
// Add any missing process methods
window.process.nextTick = window.process.nextTick || function(fn) {
setTimeout(fn, 0);
if (!window.process) {
window.process = {
env: {
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');
})();
EOF
ENDPATCHES
# Create the static HTML files with scripts pre-injected
for app_dir in photos accounts auth cast; do