Skip to content

Commit

Permalink
fix(ref-imp): #760 - Fixed long-form resolution not verifying delta size
Browse files Browse the repository at this point in the history
  • Loading branch information
thehenrytsai committed Aug 13, 2020
1 parent be481c0 commit fc1e8a9
Show file tree
Hide file tree
Showing 20 changed files with 122 additions and 78 deletions.
2 changes: 1 addition & 1 deletion lib/core/versions/0.9.0/AnchorFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default class AnchorFile {

let anchorFileDecompressedBuffer;
try {
const maxAllowedDecompressedSizeInBytes = ProtocolParameters.maxAnchorFileSizeInBytes * Compressor.estimatedDecomporessionMultiplier;
const maxAllowedDecompressedSizeInBytes = ProtocolParameters.maxAnchorFileSizeInBytes * Compressor.estimatedDecompressionMultiplier;
anchorFileDecompressedBuffer = await Compressor.decompress(anchorFileBuffer, maxAllowedDecompressedSizeInBytes);
} catch (e) {
throw SidetreeError.createFromError(ErrorCode.AnchorFileDecompressionFailure, e);
Expand Down
12 changes: 3 additions & 9 deletions lib/core/versions/0.9.0/ChunkFile.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ChunkFileModel from './models/ChunkFileModel';
import Compressor from './util/Compressor';
import CreateOperation from './CreateOperation';
import Delta from './Delta';
import ErrorCode from './ErrorCode';
import JsonAsync from './util/JsonAsync';
import ProtocolParameters from './ProtocolParameters';
Expand All @@ -23,7 +24,7 @@ export default class ChunkFile {
): Promise<ChunkFileModel> {

let endTimer = timeSpan();
const maxAllowedDecompressedSizeInBytes = ProtocolParameters.maxChunkFileSizeInBytes * Compressor.estimatedDecomporessionMultiplier;
const maxAllowedDecompressedSizeInBytes = ProtocolParameters.maxChunkFileSizeInBytes * Compressor.estimatedDecompressionMultiplier;
const decompressedChunkFileBuffer = await Compressor.decompress(chunkFileBuffer, maxAllowedDecompressedSizeInBytes);
const chunkFileObject = await JsonAsync.parse(decompressedChunkFileBuffer);
console.info(`Parsed chunk file in ${endTimer.rounded()} ms.`);
Expand Down Expand Up @@ -53,15 +54,8 @@ export default class ChunkFile {
throw new SidetreeError(ErrorCode.ChunkFileDeltasNotArrayOfStrings, 'Invalid chunk file, deltas property is not an array of strings.');
}

const deltaBuffer = Buffer.from(encodedDelta);

// Verify size of each delta does not exceed the maximum allowed limit.
if (deltaBuffer.length > ProtocolParameters.maxDeltaSizeInBytes) {
throw new SidetreeError(
ErrorCode.ChunkFileDeltaSizeExceedsLimit,
`Operation size of ${deltaBuffer.length} bytes exceeds the allowed limit of ${ProtocolParameters.maxDeltaSizeInBytes} bytes.`
);
}
Delta.validateEncodedDeltaSize(encodedDelta);
}
}

Expand Down
22 changes: 22 additions & 0 deletions lib/core/versions/0.9.0/Delta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import ErrorCode from './ErrorCode';
import ProtocolParameters from './ProtocolParameters';
import SidetreeError from '../../../common/SidetreeError';

/**
* Class containing reusable operation delta functionalities.
*/
export default class Delta {

/**
* Validates size of the encoded delta string.
* @throws `SidetreeError` if fails validation.
*/
public static validateEncodedDeltaSize (encodedDelta: string) {
const deltaBuffer = Buffer.from(encodedDelta);
if (deltaBuffer.length > ProtocolParameters.maxDeltaSizeInBytes) {
const errorMessage = `${deltaBuffer.length} bytes of 'delta' exceeded limit of ${ProtocolParameters.maxDeltaSizeInBytes} bytes.`;
console.info(errorMessage);
throw new SidetreeError(ErrorCode.DeltaExceedsMaximumSize, errorMessage);
}
}
}
6 changes: 5 additions & 1 deletion lib/core/versions/0.9.0/Did.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import CreateOperation from './CreateOperation';
import Delta from './Delta';
import ErrorCode from './ErrorCode';
import Multihash from './Multihash';
import OperationType from '../../enums/OperationType';
Expand Down Expand Up @@ -72,7 +73,7 @@ export default class Did {
const createOperation = await Did.constructCreateOperationFromInitialState(initialState);

// NOTE: we cannot use the unique suffix directly from `createOperation.didUniqueSuffix` for comparison,
// becasue a given long-form DID may have been created long ago,
// because a given long-form DID may have been created long ago,
// thus this version of `CreateOperation.parse()` maybe using a different hashing algorithm than that of the unique DID suffix (short-form).
// So we compute the suffix data hash again using the hashing algorithm used by the given unique DID suffix (short-form).
const suffixDataHashMatchesUniqueSuffix = Multihash.isValidHash(createOperation.encodedSuffixData, did.uniqueSuffix);
Expand Down Expand Up @@ -147,6 +148,9 @@ export default class Did {
const initialStateParts = initialState.split('.');
const suffixData = initialStateParts[0];
const delta = initialStateParts[1];

Delta.validateEncodedDeltaSize(delta);

const createOperationRequest = {
type: OperationType.Create,
suffix_data: suffixData,
Expand Down
20 changes: 9 additions & 11 deletions lib/core/versions/0.9.0/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export default {
CasFileNotFound: 'cas_file_not_found',
CasFileTooLarge: 'cas_file_too_large',
CasNotReachable: 'cas_not_reachable',
ChunkFileDeltaSizeExceedsLimit: 'chunk_file_delta_size_exceeds_limit',
ChunkFileDeltasNotArrayOfStrings: 'chunk_file_deltas_not_array_of_string',
ChunkFileDeltasPropertyNotArray: 'chunk_file_deltas_property_not_array',
ChunkFileUnexpectedProperty: 'chunk_file_unexpected_property',
Expand All @@ -38,7 +37,13 @@ export default {
CreateOperationSuffixDataTypeIsNotString: 'create_operation_suffix_data_type_is_not_string',
CreateOperationSuffixDataTypeLengtGreaterThanFour: 'create_operation_suffix_data_type_length_greater_than_four',
CreateOperationTypeIncorrect: 'create_operation_type_incorrect',
DeltaMissingOrNotString: 'delta_missting_or_not_string',
DeactivateOperationMissingOrInvalidDidUniqueSuffix: 'deactivate_operation_missing_or_invalid_did_unique_suffix',
DeactivateOperationMissingOrUnknownProperty: 'deactivate_operation_missing_or_unknown_property',
DeactivateOperationSignedDataMissingOrUnknownProperty: 'deactivate_operation_signed_data_missing_or_unknown_property',
DeactivateOperationSignedDidUniqueSuffixMismatch: 'deactivate_operation_signed_did_unique_suffix_mismatch',
DeactivateOperationTypeIncorrect: 'deactivate_operation_type_incorrect',
DeltaMissingOrNotString: 'delta_missing_or_not_string',
DeltaExceedsMaximumSize: 'delta_exceeds_maximum_size',
DeltaMissingOrUnknownProperty: 'delta_missing_or_unknown_property',
DidIncorrectPrefix: 'did_incorrect_prefix',
DidInitialStateValueContainsMoreThanOneDot: 'did_initial_state_value_contains_more_than_one_dot',
Expand Down Expand Up @@ -81,7 +86,6 @@ export default {
DocumentComposerServiceNotArray: 'document_composer_service_not_array',
DocumentComposerUnknownPropertyInDocument: 'document_composer_unknown_property_in_document',
DocumentComposerUpdateOperationDocumentPatchesNotArray: 'document_composer_update_operation_document_patch_not_array',
DocumentIncorretEncodedFormat: 'document_incorrect_encoded_format',
DocumentNotJson: 'document_not_json',
DocumentNotValidOriginalDocument: 'document_not_valid_original_document',
EncoderValidateBase64UrlStringInputNotBase64UrlString: 'encoder_validate_base64url_string_input_not_base64url_string',
Expand Down Expand Up @@ -125,13 +129,7 @@ export default {
RecoverOperationMissingOrUnknownProperty: 'recover_operation_missing_or_unknown_property',
RecoverOperationSignedDataMissingOrUnknownProperty: 'recover_operation_signed_data_missing_or_unknown_property',
RecoverOperationTypeIncorrect: 'recover_operation_type_incorrect',
RequestHandlerDeltaExceedsMaximumSize: 'request_handler_patch_data_exceeds_maximum_size',
RequestHandlerUnknownOperationType: 'request_handler_unknown_operation_type',
DeactivateOperationMissingOrInvalidDidUniqueSuffix: 'deactivate_operation_missing_or_invalid_did_unique_suffix',
DeactivateOperationMissingOrUnknownProperty: 'deactivate_operation_missing_or_unknown_property',
DeactivateOperationSignedDataMissingOrUnknownProperty: 'deactivate_operation_signed_data_missing_or_unknown_property',
DeactivateOperationSignedDidUniqueSuffixMismatch: 'deactivate_operation_signed_did_unique_suffix_mismatch',
DeactivateOperationTypeIncorrect: 'deactivate_operation_type_incorrect',
TransactionFeePaidInvalid: 'transaction_fee_paid_is_invalid',
TransactionFeePaidLessThanNormalizedFee: 'transaction_fee_paid_less_than_normalized_fee',
TransactionProcessorPaidOperationCountExceedsLimit: 'transaction_processor_paid_operation_count_exceeds_limit',
Expand All @@ -141,6 +139,6 @@ export default {
UpdateOperationSignedDataHasMissingOrUnknownProperty: 'update_operation_signed_data_has_missing_or_unknown_property',
UpdateOperationTypeIncorrect: 'update_operation_type_incorrect',
ValueTimeLockVerifierInvalidNumberOfOperations: 'value_time_lock_verifierInvalid_number_of_operations',
ValueTimeLockVerifierTransactionTimeOutsideLockRange: 'value_time_lock_verifiertarget_transaction_time_outside_lock_range',
ValueTimeLockVerifierTransactionWriterLockOwnerMismatch: 'value_time_lock_verifiertransaction_owner_lock_writer_mismatch'
ValueTimeLockVerifierTransactionTimeOutsideLockRange: 'value_time_lock_verifier_transaction_time_outside_lock_range',
ValueTimeLockVerifierTransactionWriterLockOwnerMismatch: 'value_time_lock_verifier_transaction_writer_lock_owner_mismatch'
};
2 changes: 1 addition & 1 deletion lib/core/versions/0.9.0/MapFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default class MapFile {

let decompressedBuffer;
try {
const maxAllowedDecompressedSizeInBytes = ProtocolParameters.maxMapFileSizeInBytes * Compressor.estimatedDecomporessionMultiplier;
const maxAllowedDecompressedSizeInBytes = ProtocolParameters.maxMapFileSizeInBytes * Compressor.estimatedDecompressionMultiplier;
decompressedBuffer = await Compressor.decompress(mapFileBuffer, maxAllowedDecompressedSizeInBytes);
} catch (error) {
throw SidetreeError.createFromError(ErrorCode.MapFileDecompressionFailure, error);
Expand Down
9 changes: 2 additions & 7 deletions lib/core/versions/0.9.0/RequestHandler.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Delta from './Delta';
import Did from './Did';
import DidState from '../../models/DidState';
import DocumentComposer from './DocumentComposer';
Expand All @@ -9,7 +10,6 @@ import Operation from './Operation';
import OperationModel from './models/OperationModel';
import OperationProcessor from './OperationProcessor';
import OperationType from '../../enums/OperationType';
import ProtocolParameters from './ProtocolParameters';
import Resolver from '../../Resolver';
import ResponseModel from '../../../common/models/ResponseModel';
import ResponseStatus from '../../../common/enums/ResponseStatus';
Expand Down Expand Up @@ -44,12 +44,7 @@ export default class RequestHandler implements IRequestHandler {
if (operationRequest.type === OperationType.Create ||
operationRequest.type === OperationType.Recover ||
operationRequest.type === OperationType.Update) {
const deltaBuffer = Buffer.from(operationRequest.delta);
if (deltaBuffer.length > ProtocolParameters.maxDeltaSizeInBytes) {
const errorMessage = `operationDdata byte size of ${deltaBuffer.length} exceeded limit of ${ProtocolParameters.maxDeltaSizeInBytes}`;
console.info(errorMessage);
throw new SidetreeError(ErrorCode.RequestHandlerDeltaExceedsMaximumSize, errorMessage);
}
Delta.validateEncodedDeltaSize(operationRequest.delta);
}

operationModel = await Operation.parse(request);
Expand Down
2 changes: 1 addition & 1 deletion lib/core/versions/0.9.0/util/Compressor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import zlib = require('zlib');
export default class Compressor {

/** The estimated ratio/multiplier of decompressed Sidetree CAS file size compared against the compressed file size. */
public static readonly estimatedDecomporessionMultiplier = 3;
public static readonly estimatedDecompressionMultiplier = 3;

private static readonly gzipAsync = util.promisify(zlib.gzip);

Expand Down
2 changes: 1 addition & 1 deletion lib/core/versions/latest/AnchorFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default class AnchorFile {

let anchorFileDecompressedBuffer;
try {
const maxAllowedDecompressedSizeInBytes = ProtocolParameters.maxAnchorFileSizeInBytes * Compressor.estimatedDecomporessionMultiplier;
const maxAllowedDecompressedSizeInBytes = ProtocolParameters.maxAnchorFileSizeInBytes * Compressor.estimatedDecompressionMultiplier;
anchorFileDecompressedBuffer = await Compressor.decompress(anchorFileBuffer, maxAllowedDecompressedSizeInBytes);
} catch (e) {
throw SidetreeError.createFromError(ErrorCode.AnchorFileDecompressionFailure, e);
Expand Down
12 changes: 3 additions & 9 deletions lib/core/versions/latest/ChunkFile.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ChunkFileModel from './models/ChunkFileModel';
import Compressor from './util/Compressor';
import CreateOperation from './CreateOperation';
import Delta from './Delta';
import ErrorCode from './ErrorCode';
import JsonAsync from './util/JsonAsync';
import ProtocolParameters from './ProtocolParameters';
Expand All @@ -23,7 +24,7 @@ export default class ChunkFile {
): Promise<ChunkFileModel> {

let endTimer = timeSpan();
const maxAllowedDecompressedSizeInBytes = ProtocolParameters.maxChunkFileSizeInBytes * Compressor.estimatedDecomporessionMultiplier;
const maxAllowedDecompressedSizeInBytes = ProtocolParameters.maxChunkFileSizeInBytes * Compressor.estimatedDecompressionMultiplier;
const decompressedChunkFileBuffer = await Compressor.decompress(chunkFileBuffer, maxAllowedDecompressedSizeInBytes);
const chunkFileObject = await JsonAsync.parse(decompressedChunkFileBuffer);
console.info(`Parsed chunk file in ${endTimer.rounded()} ms.`);
Expand Down Expand Up @@ -53,15 +54,8 @@ export default class ChunkFile {
throw new SidetreeError(ErrorCode.ChunkFileDeltasNotArrayOfStrings, 'Invalid chunk file, deltas property is not an array of strings.');
}

const deltaBuffer = Buffer.from(encodedDelta);

// Verify size of each delta does not exceed the maximum allowed limit.
if (deltaBuffer.length > ProtocolParameters.maxDeltaSizeInBytes) {
throw new SidetreeError(
ErrorCode.ChunkFileDeltaSizeExceedsLimit,
`Operation size of ${deltaBuffer.length} bytes exceeds the allowed limit of ${ProtocolParameters.maxDeltaSizeInBytes} bytes.`
);
}
Delta.validateEncodedDeltaSize(encodedDelta);
}
}

Expand Down
22 changes: 22 additions & 0 deletions lib/core/versions/latest/Delta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import ErrorCode from './ErrorCode';
import ProtocolParameters from './ProtocolParameters';
import SidetreeError from '../../../common/SidetreeError';

/**
* Class containing reusable operation delta functionalities.
*/
export default class Delta {

/**
* Validates size of the encoded delta string.
* @throws `SidetreeError` if fails validation.
*/
public static validateEncodedDeltaSize (encodedDelta: string) {
const deltaBuffer = Buffer.from(encodedDelta);
if (deltaBuffer.length > ProtocolParameters.maxDeltaSizeInBytes) {
const errorMessage = `${deltaBuffer.length} bytes of 'delta' exceeded limit of ${ProtocolParameters.maxDeltaSizeInBytes} bytes.`;
console.info(errorMessage);
throw new SidetreeError(ErrorCode.DeltaExceedsMaximumSize, errorMessage);
}
}
}
6 changes: 5 additions & 1 deletion lib/core/versions/latest/Did.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import CreateOperation from './CreateOperation';
import Delta from './Delta';
import ErrorCode from './ErrorCode';
import Multihash from './Multihash';
import OperationType from '../../enums/OperationType';
Expand Down Expand Up @@ -72,7 +73,7 @@ export default class Did {
const createOperation = await Did.constructCreateOperationFromInitialState(initialState);

// NOTE: we cannot use the unique suffix directly from `createOperation.didUniqueSuffix` for comparison,
// becasue a given long-form DID may have been created long ago,
// because a given long-form DID may have been created long ago,
// thus this version of `CreateOperation.parse()` maybe using a different hashing algorithm than that of the unique DID suffix (short-form).
// So we compute the suffix data hash again using the hashing algorithm used by the given unique DID suffix (short-form).
const suffixDataHashMatchesUniqueSuffix = Multihash.isValidHash(createOperation.encodedSuffixData, did.uniqueSuffix);
Expand Down Expand Up @@ -147,6 +148,9 @@ export default class Did {
const initialStateParts = initialState.split('.');
const suffixData = initialStateParts[0];
const delta = initialStateParts[1];

Delta.validateEncodedDeltaSize(delta);

const createOperationRequest = {
type: OperationType.Create,
suffix_data: suffixData,
Expand Down
Loading

0 comments on commit fc1e8a9

Please sign in to comment.