fix: direct-download reads the sealed object for its CEK header
getObject decrypts by magic, so feeding its plaintext into openDirectHeader always failed. getSealedObject exposes raw bytes for the header read; DirectStorageMembers and the test fakes carry the new member.
This commit is contained in:
parent
0a1c3b392b
commit
a7c3f26a4f
2 changed files with 11 additions and 2 deletions
|
|
@ -665,7 +665,7 @@ export class GroupFilesService {
|
|||
const storage = this.storage();
|
||||
const [url, frame] = await Promise.all([
|
||||
storage.presign(storageKey, DIRECT_GET_TTL_SECONDS),
|
||||
storage.getObject(storageKey).then((stored) => storage.openDirectHeader(stored)),
|
||||
storage.getSealedObject(storageKey).then((stored) => storage.openDirectHeader(stored)),
|
||||
]).catch(() => { throw new ServiceUnavailableException("file storage is unavailable"); });
|
||||
return { url, cekB64: frame.toString("base64"), frame: "SPENC2", sizeBytes: item.sizeBytes };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,12 +48,14 @@ export interface StorageService {
|
|||
headObject(key: string): Promise<{ exists: boolean; contentLength: number | null }>;
|
||||
/** Direct-to-R2: unwrap the frame's CEK (header only) for /files/:id/direct-download. */
|
||||
openDirectHeader(stored: Buffer): Promise<Buffer>;
|
||||
/** Direct-to-R2: raw still-sealed object bytes — the CEK header lives only in the sealed form. */
|
||||
getSealedObject(key: string): Promise<Buffer>;
|
||||
}
|
||||
|
||||
/** The direct-only members of StorageService, as one spreadable group. */
|
||||
export type DirectStorageMembers = Pick<
|
||||
StorageService,
|
||||
"directPublicJwk" | "presignPut" | "headObject" | "openDirectHeader"
|
||||
"directPublicJwk" | "presignPut" | "headObject" | "openDirectHeader" | "getSealedObject"
|
||||
>;
|
||||
|
||||
/**
|
||||
|
|
@ -70,6 +72,7 @@ export function directStorageDisabled(): DirectStorageMembers {
|
|||
presignPut: unavailable,
|
||||
headObject: unavailable,
|
||||
openDirectHeader: unavailable,
|
||||
getSealedObject: unavailable,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -514,6 +517,12 @@ export class S3Storage implements StorageService {
|
|||
if (this.directKey === null) throw missingDirectKey();
|
||||
return unwrapDirectCek(stored, this.directKey.privateKey);
|
||||
}
|
||||
|
||||
async getSealedObject(key: string): Promise<Buffer> {
|
||||
const res = await this.client.send(new GetObjectCommand({ Bucket: this.bucket, Key: key }));
|
||||
if (res.Body === undefined) throw new Error(`empty body for ${key}`);
|
||||
return Buffer.from(await res.Body.transformToByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
/** Build a DirectKeyPair from an already-loaded private key (config + tests). */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue