diff --git a/pkg/api/protocol/protocol.go b/pkg/api/protocol/protocol.go index ae933e45..29020caa 100644 --- a/pkg/api/protocol/protocol.go +++ b/pkg/api/protocol/protocol.go @@ -29,8 +29,8 @@ type Protocol struct { // GenesisTime is inclusive starting logical blockchain time that this protocol applies to. // (e.g. block number in a blockchain) GenesisTime uint64 `json:"genesisTime"` - // MultihashAlgorithm is multihash algorithm code. - MultihashAlgorithm uint `json:"multihashAlgorithm"` + // MultihashAlgorithms are supported multihash algorithm codes + MultihashAlgorithms []uint `json:"multihashAlgorithms"` // MaxOperationCount defines maximum number of operations per batch. MaxOperationCount uint `json:"maxOperationCount"` // MaxOperationSize is maximum operation size in bytes (used to reject operations before parsing them) diff --git a/pkg/hashing/hash.go b/pkg/hashing/hash.go index 35b8bab7..ec054b22 100644 --- a/pkg/hashing/hash.go +++ b/pkg/hashing/hash.go @@ -56,14 +56,20 @@ func IsSupportedMultihash(encodedMultihash string) bool { return multihash.ValidCode(code) } -// IsComputedUsingMultihashAlgorithm checks to see if the given encoded hash has been hashed using multihash code. -func IsComputedUsingMultihashAlgorithm(encodedMultihash string, code uint64) bool { +// IsComputedUsingMultihashAlgorithms checks to see if the given encoded hash has been hashed using one of supplied code. +func IsComputedUsingMultihashAlgorithms(encodedMultihash string, codes []uint) bool { mhCode, err := GetMultihashCode(encodedMultihash) if err != nil { return false } - return mhCode == code + for _, supported := range codes { + if mhCode == uint64(supported) { + return true + } + } + + return false } // GetMultihashCode returns multihash code from encoded multihash. diff --git a/pkg/hashing/hash_test.go b/pkg/hashing/hash_test.go index 1fa83a4e..b0553fa0 100644 --- a/pkg/hashing/hash_test.go +++ b/pkg/hashing/hash_test.go @@ -72,14 +72,14 @@ func TestIsComputedUsingHashAlgorithm(t *testing.T) { require.NotNil(t, hash) key := encoder.EncodeToString(hash) - ok := IsComputedUsingMultihashAlgorithm(key, sha2_256) + ok := IsComputedUsingMultihashAlgorithms(key, []uint{sha2_256}) require.True(t, ok) // use random code to fail - ok = IsComputedUsingMultihashAlgorithm(key, 55) + ok = IsComputedUsingMultihashAlgorithms(key, []uint{55}) require.False(t, ok) - ok = IsComputedUsingMultihashAlgorithm("invalid", sha2_256) + ok = IsComputedUsingMultihashAlgorithms("invalid", []uint{sha2_256}) require.False(t, ok) } diff --git a/pkg/mocks/protocol.go b/pkg/mocks/protocol.go index 80c43b31..ba9337d1 100644 --- a/pkg/mocks/protocol.go +++ b/pkg/mocks/protocol.go @@ -140,7 +140,7 @@ func GetDefaultProtocolParameters() protocol.Protocol { //nolint:gomnd return protocol.Protocol{ GenesisTime: 0, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, MaxOperationCount: 2, MaxOperationSize: MaxOperationByteSize, MaxOperationHashLength: 100, diff --git a/pkg/processor/processor_test.go b/pkg/processor/processor_test.go index 615642fd..407b5cec 100644 --- a/pkg/processor/processor_test.go +++ b/pkg/processor/processor_test.go @@ -166,7 +166,7 @@ func TestUpdateDocument(t *testing.T) { pubJWK, err := pubkey.GetPublicKeyJWK(&updateKey.PublicKey) require.NoError(t, err) - rv, err := commitment.GetRevealValue(pubJWK, getProtocol(1).MultihashAlgorithm) + rv, err := commitment.GetRevealValue(pubJWK, getProtocol(1).MultihashAlgorithms[0]) require.NoError(t, err) // protocol value for hashing algorithm changed at block 100 @@ -210,7 +210,7 @@ func TestUpdateDocument(t *testing.T) { require.NoError(t, err) // previous operation commit value was calculated with protocol value at block 50 - rv, err := commitment.GetRevealValue(pubJWK, getProtocol(50).MultihashAlgorithm) + rv, err := commitment.GetRevealValue(pubJWK, getProtocol(50).MultihashAlgorithms[0]) require.NoError(t, err) // protocol value for hashing algorithm changed at block 100 @@ -478,7 +478,7 @@ func TestRecover(t *testing.T) { pubJWK, err := pubkey.GetPublicKeyJWK(&recoveryKey.PublicKey) require.NoError(t, err) - rv, err := commitment.GetRevealValue(pubJWK, getProtocol(1).MultihashAlgorithm) + rv, err := commitment.GetRevealValue(pubJWK, getProtocol(1).MultihashAlgorithms[0]) require.NoError(t, err) // hashing algorithm changed at block 100 @@ -538,7 +538,7 @@ func TestRecover(t *testing.T) { pubJWK, err := pubkey.GetPublicKeyJWK(&nextRecoveryKey.PublicKey) require.NoError(t, err) - rv, err := commitment.GetRevealValue(pubJWK, getProtocol(50).MultihashAlgorithm) + rv, err := commitment.GetRevealValue(pubJWK, getProtocol(50).MultihashAlgorithms[0]) require.NoError(t, err) // hashing algorithm changed at block 100 @@ -906,7 +906,7 @@ func getUpdateOperationWithSigner(s client.Signer, privateKey *ecdsa.PrivateKey, Patches: []patch.Patch{jsonPatch}, } - deltaHash, err := hashing.CalculateModelMultihash(delta, getProtocol(blockNumber).MultihashAlgorithm) + deltaHash, err := hashing.CalculateModelMultihash(delta, getProtocol(blockNumber).MultihashAlgorithms[0]) if err != nil { return nil, nil, err } @@ -926,7 +926,7 @@ func getUpdateOperationWithSigner(s client.Signer, privateKey *ecdsa.PrivateKey, return nil, nil, err } - rv, err := commitment.GetRevealValue(updatePubKey, getProtocol(blockNumber).MultihashAlgorithm) + rv, err := commitment.GetRevealValue(updatePubKey, getProtocol(blockNumber).MultihashAlgorithms[0]) if err != nil { return nil, nil, err } @@ -955,7 +955,7 @@ func generateKeyAndCommitment(p protocol.Protocol) (*ecdsa.PrivateKey, string, e return nil, "", err } - c, err := commitment.GetCommitment(pubKey, p.MultihashAlgorithm) + c, err := commitment.GetCommitment(pubKey, p.MultihashAlgorithms[0]) if err != nil { return nil, "", err } @@ -1046,7 +1046,7 @@ func getRecoverOperationWithSigner(signer client.Signer, recoveryKey, updateKey } func getRecoverRequest(signer client.Signer, deltaModel *model.DeltaModel, signedDataModel *model.RecoverSignedDataModel, blockNum uint64) (*model.RecoverRequest, error) { - deltaHash, err := hashing.CalculateModelMultihash(deltaModel, getProtocol(blockNum).MultihashAlgorithm) + deltaHash, err := hashing.CalculateModelMultihash(deltaModel, getProtocol(blockNum).MultihashAlgorithms[0]) if err != nil { return nil, err } @@ -1058,7 +1058,7 @@ func getRecoverRequest(signer client.Signer, deltaModel *model.DeltaModel, signe return nil, err } - rv, err := commitment.GetRevealValue(signedDataModel.RecoveryKey, getProtocol(blockNum).MultihashAlgorithm) + rv, err := commitment.GetRevealValue(signedDataModel.RecoveryKey, getProtocol(blockNum).MultihashAlgorithms[0]) if err != nil { return nil, err } @@ -1087,7 +1087,7 @@ func getDefaultRecoverRequest(signer client.Signer, recoveryKey, updateKey *ecds return nil, nil, err } - deltaHash, err := hashing.CalculateModelMultihash(delta, p.MultihashAlgorithm) + deltaHash, err := hashing.CalculateModelMultihash(delta, p.MultihashAlgorithms[0]) if err != nil { return nil, nil, err } @@ -1253,7 +1253,7 @@ func getCommitment(key *ecdsa.PrivateKey, p protocol.Protocol) (string, error) { return "", err } - return commitment.GetCommitment(pubKey, p.MultihashAlgorithm) + return commitment.GetCommitment(pubKey, p.MultihashAlgorithms[0]) } func getSuffixData(privateKey *ecdsa.PrivateKey, delta *model.DeltaModel, p protocol.Protocol) (*model.SuffixDataModel, error) { @@ -1262,7 +1262,7 @@ func getSuffixData(privateKey *ecdsa.PrivateKey, delta *model.DeltaModel, p prot return nil, err } - deltaHash, err := hashing.CalculateModelMultihash(delta, p.MultihashAlgorithm) + deltaHash, err := hashing.CalculateModelMultihash(delta, p.MultihashAlgorithms[0]) if err != nil { return nil, err } @@ -1319,7 +1319,7 @@ func newMockProtocolClient() *mocks.MockProtocolClient { //nolint:gomnd latest := protocol.Protocol{ GenesisTime: 100, - MultihashAlgorithm: sha2_512, + MultihashAlgorithms: []uint{sha2_512, sha2_256}, MaxOperationCount: 2, MaxOperationSize: mocks.MaxOperationByteSize, MaxOperationHashLength: 100, diff --git a/pkg/versions/0_1/client/create.go b/pkg/versions/0_1/client/create.go index 13e9b4e2..114c3cc1 100644 --- a/pkg/versions/0_1/client/create.go +++ b/pkg/versions/0_1/client/create.go @@ -100,11 +100,11 @@ func validateCreateRequest(info *CreateRequestInfo) error { return fmt.Errorf("multihash[%d] not supported", info.MultihashCode) } - if !hashing.IsComputedUsingMultihashAlgorithm(info.RecoveryCommitment, uint64(info.MultihashCode)) { + if !hashing.IsComputedUsingMultihashAlgorithms(info.RecoveryCommitment, []uint{info.MultihashCode}) { return errors.New("next recovery commitment is not computed with the specified hash algorithm") } - if !hashing.IsComputedUsingMultihashAlgorithm(info.UpdateCommitment, uint64(info.MultihashCode)) { + if !hashing.IsComputedUsingMultihashAlgorithms(info.UpdateCommitment, []uint{info.MultihashCode}) { return errors.New("next update commitment is not computed with the specified hash algorithm") } diff --git a/pkg/versions/0_1/model/util.go b/pkg/versions/0_1/model/util.go index b0c48261..00f96fdd 100644 --- a/pkg/versions/0_1/model/util.go +++ b/pkg/versions/0_1/model/util.go @@ -7,10 +7,12 @@ SPDX-License-Identifier: Apache-2.0 package model import ( + "errors" "fmt" "github.com/trustbloc/sidetree-core-go/pkg/api/operation" "github.com/trustbloc/sidetree-core-go/pkg/canonicalizer" + "github.com/trustbloc/sidetree-core-go/pkg/hashing" ) // GetAnchoredOperation is utility method for converting operation model into anchored operation. @@ -65,3 +67,21 @@ func GetAnchoredOperation(op *Operation) (*operation.AnchoredOperation, error) { OperationBuffer: operationBuffer, }, nil } + +// GetUniqueSuffix calculates unique suffix from suffix data and multihash algorithms. +func GetUniqueSuffix(model *SuffixDataModel, algs []uint) (string, error) { + if len(algs) == 0 { + return "", errors.New("failed to calculate unique suffix: algorithm not provided") + } + + // Even though protocol supports the list of multihashing algorithms in this protocol version (v1) we can have + // only one multihashing algorithm. Later versions may have multiple values for backward compatibility. + // At that point (version 2) the spec will hopefully better define how to handle this scenarios: + // https://github.com/decentralized-identity/sidetree/issues/965 + encodedComputedMultihash, err := hashing.CalculateModelMultihash(model, algs[0]) + if err != nil { + return "", fmt.Errorf("failed to calculate unique suffix: %s", err.Error()) + } + + return encodedComputedMultihash, nil +} diff --git a/pkg/versions/0_1/model/util_test.go b/pkg/versions/0_1/model/util_test.go new file mode 100644 index 00000000..e82d7ef1 --- /dev/null +++ b/pkg/versions/0_1/model/util_test.go @@ -0,0 +1,141 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package model + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/trustbloc/sidetree-core-go/pkg/api/operation" +) + +const suffix = "suffix" + +func TestGetAnchoredOperation(t *testing.T) { + t.Run("success - create", func(t *testing.T) { + op := &Operation{ + Type: operation.TypeCreate, + UniqueSuffix: suffix, + SuffixData: &SuffixDataModel{ + RecoveryCommitment: "rc", + DeltaHash: "dh", + }, + Delta: &DeltaModel{ + UpdateCommitment: "uc", + }, + } + + opBuffer := `{"delta":{"updateCommitment":"uc"},"suffixData":{"deltaHash":"dh","recoveryCommitment":"rc"},"type":"create"}` + + anchored, err := GetAnchoredOperation(op) + require.NoError(t, err) + require.NotNil(t, anchored) + + require.Equal(t, op.Type, anchored.Type) + require.Equal(t, opBuffer, string(anchored.OperationBuffer)) + require.Equal(t, suffix, anchored.UniqueSuffix) + }) + + t.Run("success - deactivate", func(t *testing.T) { + op := &Operation{ + Type: operation.TypeDeactivate, + UniqueSuffix: suffix, + RevealValue: "rv", + SignedData: "jws", + } + + opBuffer := `{"didSuffix":"suffix","revealValue":"rv","signedData":"jws","type":"deactivate"}` + + anchored, err := GetAnchoredOperation(op) + require.NoError(t, err) + require.NotNil(t, anchored) + + require.Equal(t, op.Type, anchored.Type) + require.Equal(t, opBuffer, string(anchored.OperationBuffer)) + require.Equal(t, suffix, anchored.UniqueSuffix) + }) + + t.Run("success - recover", func(t *testing.T) { + op := &Operation{ + Type: operation.TypeRecover, + UniqueSuffix: suffix, + RevealValue: "rv", + SignedData: "jws", + Delta: &DeltaModel{ + UpdateCommitment: "uc", + }, + } + + opBuffer := `{"delta":{"updateCommitment":"uc"},"didSuffix":"suffix","revealValue":"rv","signedData":"jws","type":"recover"}` + + anchored, err := GetAnchoredOperation(op) + require.NoError(t, err) + require.NotNil(t, anchored) + require.Equal(t, op.Type, anchored.Type) + + require.Equal(t, opBuffer, string(anchored.OperationBuffer)) + require.Equal(t, suffix, anchored.UniqueSuffix) + }) + + t.Run("success - update", func(t *testing.T) { + op := &Operation{ + Type: operation.TypeUpdate, + UniqueSuffix: suffix, + RevealValue: "rv", + SignedData: "jws", + Delta: &DeltaModel{ + UpdateCommitment: "uc", + }, + } + + opBuffer := `{"delta":{"updateCommitment":"uc"},"didSuffix":"suffix","revealValue":"rv","signedData":"jws","type":"update"}` + anchored, err := GetAnchoredOperation(op) + require.NoError(t, err) + require.NotNil(t, anchored) + require.Equal(t, anchored.Type, op.Type) + + require.Equal(t, opBuffer, string(anchored.OperationBuffer)) + require.Equal(t, suffix, anchored.UniqueSuffix) + }) + + t.Run("error - type not supported", func(t *testing.T) { + op := &Operation{Type: "other"} + + anchored, err := GetAnchoredOperation(op) + require.Error(t, err) + require.Nil(t, anchored) + require.Contains(t, err.Error(), "operation type other not supported for anchored operation") + }) +} + +func TestGetUniqueSuffix(t *testing.T) { + s := &SuffixDataModel{ + RecoveryCommitment: "rc", + DeltaHash: "dh", + } + + t.Run("success", func(t *testing.T) { + uniqueSuffix, err := GetUniqueSuffix(s, []uint{18}) + require.NoError(t, err) + require.NotEmpty(t, uniqueSuffix) + }) + + t.Run("error - algorithm not provided", func(t *testing.T) { + uniqueSuffix, err := GetUniqueSuffix(s, []uint{}) + require.Error(t, err) + require.Empty(t, uniqueSuffix) + require.Contains(t, err.Error(), "failed to calculate unique suffix: algorithm not provided") + }) + + t.Run("error - algorithm not supported", func(t *testing.T) { + uniqueSuffix, err := GetUniqueSuffix(s, []uint{55}) + require.Error(t, err) + require.Empty(t, uniqueSuffix) + require.Contains(t, err.Error(), "failed to calculate unique suffix: algorithm not supported") + }) +} diff --git a/pkg/versions/0_1/operationapplier/operationapplier_test.go b/pkg/versions/0_1/operationapplier/operationapplier_test.go index 65a905da..bc07145f 100644 --- a/pkg/versions/0_1/operationapplier/operationapplier_test.go +++ b/pkg/versions/0_1/operationapplier/operationapplier_test.go @@ -44,7 +44,7 @@ const ( var ( p = protocol.Protocol{ GenesisTime: 0, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, MaxOperationCount: 2, MaxOperationSize: 2000, MaxOperationHashLength: 100, diff --git a/pkg/versions/0_1/operationparser/commitment_test.go b/pkg/versions/0_1/operationparser/commitment_test.go index 56d97bd7..6e2de135 100644 --- a/pkg/versions/0_1/operationparser/commitment_test.go +++ b/pkg/versions/0_1/operationparser/commitment_test.go @@ -126,7 +126,7 @@ func TestParser_GetRevealValue(t *testing.T) { pubJWK, err := pubkey.GetPublicKeyJWK(&recoveryKey.PublicKey) require.NoError(t, err) - expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithm) + expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithms[0]) require.NoError(t, err) require.Equal(t, rv, expected) @@ -143,7 +143,7 @@ func TestParser_GetRevealValue(t *testing.T) { pubJWK, err := pubkey.GetPublicKeyJWK(&recoveryKey.PublicKey) require.NoError(t, err) - expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithm) + expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithms[0]) require.NoError(t, err) require.Equal(t, rv, expected) @@ -160,7 +160,7 @@ func TestParser_GetRevealValue(t *testing.T) { pubJWK, err := pubkey.GetPublicKeyJWK(&updateKey.PublicKey) require.NoError(t, err) - expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithm) + expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithms[0]) require.NoError(t, err) require.Equal(t, rv, expected) @@ -206,7 +206,7 @@ func generateRecoverRequest(recoveryKey *ecdsa.PrivateKey, recoveryCommitment st RecoveryCommitment: recoveryCommitment, UpdateCommitment: updateCommitment, // not evaluated in operation getting commitment/reveal value RecoveryKey: jwk, - MultihashCode: p.MultihashAlgorithm, + MultihashCode: p.MultihashAlgorithms[0], Signer: ecsigner.New(recoveryKey, "ES256", ""), RevealValue: rv, } @@ -219,7 +219,7 @@ func generateCreateRequest(recoveryCommitment, updateCommitment string, p protoc OpaqueDocument: `{"test":"value"}`, RecoveryCommitment: recoveryCommitment, UpdateCommitment: updateCommitment, - MultihashCode: p.MultihashAlgorithm, + MultihashCode: p.MultihashAlgorithms[0], } return client.NewCreateRequest(info) @@ -268,7 +268,7 @@ func generateUpdateRequest(updateKey *ecdsa.PrivateKey, updateCommitment string, UpdateCommitment: updateCommitment, UpdateKey: jwk, Patches: []patch.Patch{testPatch}, - MultihashCode: p.MultihashAlgorithm, + MultihashCode: p.MultihashAlgorithms[0], RevealValue: rv, } @@ -286,7 +286,7 @@ func generateKeyAndCommitment(p protocol.Protocol) (*ecdsa.PrivateKey, string, e return nil, "", err } - c, err := commitment.GetCommitment(pubKey, p.MultihashAlgorithm) + c, err := commitment.GetCommitment(pubKey, p.MultihashAlgorithms[0]) if err != nil { return nil, "", err } diff --git a/pkg/versions/0_1/operationparser/create.go b/pkg/versions/0_1/operationparser/create.go index 41bdf4e2..2533ee79 100644 --- a/pkg/versions/0_1/operationparser/create.go +++ b/pkg/versions/0_1/operationparser/create.go @@ -49,7 +49,7 @@ func (p *Parser) ParseCreateOperation(request []byte, batch bool) (*model.Operat } } - uniqueSuffix, err := hashing.CalculateModelMultihash(schema.SuffixData, p.MultihashAlgorithm) + uniqueSuffix, err := model.GetUniqueSuffix(schema.SuffixData, p.MultihashAlgorithms) if err != nil { return nil, err } @@ -111,8 +111,8 @@ func (p *Parser) validateMultihash(mh, alias string) error { return fmt.Errorf("%s length[%d] exceeds maximum hash length[%d]", alias, len(mh), p.MaxOperationHashLength) } - if !hashing.IsComputedUsingMultihashAlgorithm(mh, uint64(p.MultihashAlgorithm)) { - return fmt.Errorf("%s is not computed with the required hash algorithm: %d", alias, p.MultihashAlgorithm) + if !hashing.IsComputedUsingMultihashAlgorithms(mh, p.MultihashAlgorithms) { + return fmt.Errorf("%s is not computed with the required hash algorithms: %d", alias, p.MultihashAlgorithms) } return nil diff --git a/pkg/versions/0_1/operationparser/create_test.go b/pkg/versions/0_1/operationparser/create_test.go index bde804a4..151be5e7 100644 --- a/pkg/versions/0_1/operationparser/create_test.go +++ b/pkg/versions/0_1/operationparser/create_test.go @@ -30,7 +30,7 @@ func TestParseCreateOperation(t *testing.T) { p := protocol.Protocol{ MaxOperationHashLength: 100, MaxDeltaSize: maxDeltaSize, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, Patches: []string{"replace", "add-public-keys", "remove-public-keys", "add-services", "remove-services", "ietf-json-patch"}, } @@ -81,7 +81,7 @@ func TestParseCreateOperation(t *testing.T) { op, err := parser.ParseCreateOperation(request, true) require.Error(t, err) - require.Contains(t, err.Error(), "recovery commitment is not computed with the required hash algorithm: 18") + require.Contains(t, err.Error(), "recovery commitment is not computed with the required hash algorithms: [18]") require.Nil(t, op) }) t.Run("missing delta", func(t *testing.T) { @@ -162,7 +162,7 @@ func TestParseCreateOperation(t *testing.T) { func TestValidateSuffixData(t *testing.T) { p := protocol.Protocol{ MaxOperationHashLength: maxHashLength, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, } parser := New(p) @@ -174,7 +174,7 @@ func TestValidateSuffixData(t *testing.T) { suffixData.DeltaHash = "" err = parser.ValidateSuffixData(suffixData) require.Error(t, err) - require.Contains(t, err.Error(), "delta hash is not computed with the required hash algorithm: 18") + require.Contains(t, err.Error(), "delta hash is not computed with the required hash algorithms: [18]") }) t.Run("invalid next recovery commitment hash", func(t *testing.T) { suffixData, err := getSuffixData() @@ -183,12 +183,12 @@ func TestValidateSuffixData(t *testing.T) { suffixData.RecoveryCommitment = "" err = parser.ValidateSuffixData(suffixData) require.Error(t, err) - require.Contains(t, err.Error(), "recovery commitment is not computed with the required hash algorithm: 18") + require.Contains(t, err.Error(), "recovery commitment is not computed with the required hash algorithms: [18]") }) t.Run("recovery commitment exceeds maximum hash length", func(t *testing.T) { lowHashLength := protocol.Protocol{ MaxOperationHashLength: 10, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, } suffixData, err := getSuffixData() @@ -206,7 +206,7 @@ func TestValidateDelta(t *testing.T) { p := protocol.Protocol{ MaxOperationHashLength: maxHashLength, MaxDeltaSize: maxDeltaSize, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, Patches: patches, } @@ -224,7 +224,7 @@ func TestValidateDelta(t *testing.T) { parserWithLowMaxDeltaSize := New(protocol.Protocol{ MaxOperationHashLength: maxHashLength, MaxDeltaSize: 50, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, Patches: patches, }) @@ -244,14 +244,14 @@ func TestValidateDelta(t *testing.T) { err = parser.ValidateDelta(delta) require.Error(t, err) require.Contains(t, err.Error(), - "update commitment is not computed with the required hash algorithm: 18") + "update commitment is not computed with the required hash algorithms: [18]") }) t.Run("update commitment exceeds maximum hash length", func(t *testing.T) { lowMaxHashLength := protocol.Protocol{ MaxOperationHashLength: 10, MaxDeltaSize: 50, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, Patches: patches, } diff --git a/pkg/versions/0_1/operationparser/deactivate_test.go b/pkg/versions/0_1/operationparser/deactivate_test.go index 692ae425..9264af45 100644 --- a/pkg/versions/0_1/operationparser/deactivate_test.go +++ b/pkg/versions/0_1/operationparser/deactivate_test.go @@ -26,7 +26,7 @@ const sha2_256 = 18 func TestParseDeactivateOperation(t *testing.T) { p := protocol.Protocol{ MaxProofSize: maxProofSize, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, MaxOperationHashLength: maxHashLength, SignatureAlgorithms: []string{"alg"}, KeyAlgorithms: []string{"crv"}, @@ -116,7 +116,7 @@ func TestParseDeactivateOperation(t *testing.T) { t.Run("error - key algorithm not supported", func(t *testing.T) { p := protocol.Protocol{ MaxProofSize: maxProofSize, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, MaxOperationHashLength: maxHashLength, SignatureAlgorithms: []string{"alg"}, KeyAlgorithms: []string{"other"}, diff --git a/pkg/versions/0_1/operationparser/operation_test.go b/pkg/versions/0_1/operationparser/operation_test.go index d961b3db..0cce4441 100644 --- a/pkg/versions/0_1/operationparser/operation_test.go +++ b/pkg/versions/0_1/operationparser/operation_test.go @@ -30,7 +30,7 @@ func TestGetOperation(t *testing.T) { MaxOperationHashLength: maxHashLength, MaxDeltaSize: maxDeltaSize, MaxProofSize: maxProofSize, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, SignatureAlgorithms: []string{"alg"}, KeyAlgorithms: []string{"crv"}, Patches: []string{"add-public-keys", "remove-public-keys", "add-services", "remove-services", "ietf-json-patch"}, @@ -94,7 +94,7 @@ func TestGetOperation(t *testing.T) { MaxDeltaSize: maxDeltaSize, MaxProofSize: maxProofSize, MaxOperationHashLength: maxHashLength, - MultihashAlgorithm: 18, + MultihashAlgorithms: []uint{sha2_256}, } operation, err := getRecoverRequestBytes() diff --git a/pkg/versions/0_1/operationparser/recover.go b/pkg/versions/0_1/operationparser/recover.go index 56c21292..45fe29eb 100644 --- a/pkg/versions/0_1/operationparser/recover.go +++ b/pkg/versions/0_1/operationparser/recover.go @@ -105,7 +105,7 @@ func (p *Parser) validateSignedDataForRecovery(signedData *model.RecoverSignedDa return err } - return validateCommitment(signedData.RecoveryKey, p.MultihashAlgorithm, signedData.RecoveryCommitment) + return p.validateCommitment(signedData.RecoveryKey, signedData.RecoveryCommitment) } func (p *Parser) parseSignedData(compactJWS string) (*internal.JSONWebSignature, error) { @@ -205,8 +205,13 @@ func contains(values []string, value string) bool { return false } -func validateCommitment(jwk *jws.JWK, multihashCode uint, nextCommitment string) error { - currentCommitment, err := commitment.GetCommitment(jwk, multihashCode) +func (p *Parser) validateCommitment(jwk *jws.JWK, nextCommitment string) error { + code, err := hashing.GetMultihashCode(nextCommitment) + if err != nil { + return err + } + + currentCommitment, err := commitment.GetCommitment(jwk, uint(code)) if err != nil { return fmt.Errorf("calculate current commitment: %s", err.Error()) } diff --git a/pkg/versions/0_1/operationparser/recover_test.go b/pkg/versions/0_1/operationparser/recover_test.go index 8323bd9d..e9134819 100644 --- a/pkg/versions/0_1/operationparser/recover_test.go +++ b/pkg/versions/0_1/operationparser/recover_test.go @@ -32,7 +32,7 @@ func TestParseRecoverOperation(t *testing.T) { MaxOperationHashLength: maxHashLength, MaxDeltaSize: maxDeltaSize, MaxProofSize: maxProofSize, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, SignatureAlgorithms: []string{"alg"}, KeyAlgorithms: []string{"crv"}, Patches: []string{"add-public-keys", "remove-public-keys", "add-services", "remove-services", "ietf-json-patch"}, @@ -196,7 +196,7 @@ func TestParseRecoverOperation(t *testing.T) { func TestValidateSignedDataForRecovery(t *testing.T) { p := protocol.Protocol{ MaxOperationHashLength: maxHashLength, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, KeyAlgorithms: []string{"crv"}, } @@ -215,19 +215,19 @@ func TestValidateSignedDataForRecovery(t *testing.T) { signed.DeltaHash = "" err := parser.validateSignedDataForRecovery(signed) require.Error(t, err) - require.Contains(t, err.Error(), "delta hash is not computed with the required hash algorithm: 18") + require.Contains(t, err.Error(), "delta hash is not computed with the required hash algorithms: [18]") }) t.Run("invalid next recovery commitment hash", func(t *testing.T) { signed := getSignedDataForRecovery() signed.RecoveryCommitment = "" err := parser.validateSignedDataForRecovery(signed) require.Error(t, err) - require.Contains(t, err.Error(), "recovery commitment is not computed with the required hash algorithm: 18") + require.Contains(t, err.Error(), "recovery commitment is not computed with the required hash algorithms: [18]") }) t.Run("recovery commitment exceeds maximum hash length", func(t *testing.T) { lowMaxHashLength := protocol.Protocol{ MaxOperationHashLength: 10, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, KeyAlgorithms: []string{"crv"}, } @@ -244,7 +244,7 @@ func TestParseSignedData(t *testing.T) { p := protocol.Protocol{ MaxProofSize: maxProofSize, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, SignatureAlgorithms: []string{"alg"}, } @@ -264,7 +264,7 @@ func TestParseSignedData(t *testing.T) { t.Run("error - proof exceeds max proof size", func(t *testing.T) { parserLowProofSize := New(protocol.Protocol{ MaxProofSize: 20, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, SignatureAlgorithms: []string{"alg"}, }) @@ -361,7 +361,7 @@ func TestValidateSigningKey(t *testing.T) { } func TestValidateRecoverRequest(t *testing.T) { - parser := New(protocol.Protocol{MaxOperationHashLength: maxHashLength, MultihashAlgorithm: 18}) + parser := New(protocol.Protocol{MaxOperationHashLength: maxHashLength, MultihashAlgorithms: []uint{sha2_256}}) t.Run("success", func(t *testing.T) { recover, err := getDefaultRecoverRequest() diff --git a/pkg/versions/0_1/operationparser/update.go b/pkg/versions/0_1/operationparser/update.go index 884430b0..1b33ec37 100644 --- a/pkg/versions/0_1/operationparser/update.go +++ b/pkg/versions/0_1/operationparser/update.go @@ -34,7 +34,7 @@ func (p *Parser) ParseUpdateOperation(request []byte, batch bool) (*model.Operat return nil, err } - err = validateCommitment(signedData.UpdateKey, p.MultihashAlgorithm, schema.Delta.UpdateCommitment) + err = p.validateCommitment(signedData.UpdateKey, schema.Delta.UpdateCommitment) if err != nil { return nil, fmt.Errorf("calculate current commitment: %s", err.Error()) } diff --git a/pkg/versions/0_1/operationparser/update_test.go b/pkg/versions/0_1/operationparser/update_test.go index 76553632..f9e435d1 100644 --- a/pkg/versions/0_1/operationparser/update_test.go +++ b/pkg/versions/0_1/operationparser/update_test.go @@ -27,7 +27,7 @@ func TestParseUpdateOperation(t *testing.T) { MaxOperationHashLength: maxHashLength, MaxDeltaSize: maxDeltaSize, MaxProofSize: maxProofSize, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, SignatureAlgorithms: []string{"alg"}, KeyAlgorithms: []string{"crv"}, Patches: []string{"add-public-keys", "remove-public-keys", "add-services", "remove-services", "ietf-json-patch"}, @@ -82,7 +82,7 @@ func TestParseUpdateOperation(t *testing.T) { require.Error(t, err) require.Nil(t, schema) require.Contains(t, err.Error(), - "update commitment is not computed with the required hash algorithm: 18") + "update commitment is not computed with the required hash algorithms: [18]") }) t.Run("invalid signed data", func(t *testing.T) { delta, err := getUpdateDelta() @@ -143,7 +143,7 @@ func TestParseSignedDataForUpdate(t *testing.T) { p := protocol.Protocol{ MaxOperationHashLength: maxHashLength, MaxProofSize: maxProofSize, - MultihashAlgorithm: sha2_256, + MultihashAlgorithms: []uint{sha2_256}, SignatureAlgorithms: []string{"alg"}, KeyAlgorithms: []string{"crv"}, } @@ -178,7 +178,7 @@ func TestParseSignedDataForUpdate(t *testing.T) { schema, err := parser.ParseSignedDataForUpdate(compactJWS) require.Error(t, err) require.Nil(t, schema) - require.Contains(t, err.Error(), "delta hash is not computed with the required hash algorithm: 18") + require.Contains(t, err.Error(), "delta hash is not computed with the required hash algorithms: [18]") }) t.Run("payload not JSON object", func(t *testing.T) { compactJWS, err := signutil.SignPayload([]byte("test"), NewMockSigner()) @@ -194,8 +194,8 @@ func TestParseSignedDataForUpdate(t *testing.T) { func TestValidateUpdateDelta(t *testing.T) { t.Run("invalid next update commitment hash", func(t *testing.T) { p := protocol.Protocol{ - MultihashAlgorithm: sha2_256, - Patches: []string{"add-public-keys", "remove-public-keys", "add-services", "remove-services", "ietf-json-patch"}, + MultihashAlgorithms: []uint{sha2_256}, + Patches: []string{"add-public-keys", "remove-public-keys", "add-services", "remove-services", "ietf-json-patch"}, } parser := New(p) @@ -207,12 +207,12 @@ func TestValidateUpdateDelta(t *testing.T) { err = parser.ValidateDelta(delta) require.Error(t, err) require.Contains(t, err.Error(), - "update commitment is not computed with the required hash algorithm") + "update commitment is not computed with the required hash algorithms") }) } func TestValidateUpdateRequest(t *testing.T) { - parser := New(protocol.Protocol{MaxOperationHashLength: maxHashLength, MultihashAlgorithm: 18}) + parser := New(protocol.Protocol{MaxOperationHashLength: maxHashLength, MultihashAlgorithms: []uint{sha2_256}}) t.Run("success", func(t *testing.T) { update, err := getDefaultUpdateRequest() diff --git a/pkg/versions/0_1/txnprovider/provider.go b/pkg/versions/0_1/txnprovider/provider.go index e35d4bd5..a77d1bf5 100644 --- a/pkg/versions/0_1/txnprovider/provider.go +++ b/pkg/versions/0_1/txnprovider/provider.go @@ -15,7 +15,6 @@ import ( "github.com/trustbloc/sidetree-core-go/pkg/api/operation" "github.com/trustbloc/sidetree-core-go/pkg/api/protocol" "github.com/trustbloc/sidetree-core-go/pkg/api/txn" - "github.com/trustbloc/sidetree-core-go/pkg/hashing" "github.com/trustbloc/sidetree-core-go/pkg/versions/0_1/model" "github.com/trustbloc/sidetree-core-go/pkg/versions/0_1/txnprovider/models" ) @@ -633,7 +632,7 @@ type coreOperations struct { Suffixes []string } -func (h *OperationProvider) parseCoreIndexOperations(cif *models.CoreIndexFile, txn *txn.SidetreeTxn) (*coreOperations, error) { +func (h *OperationProvider) parseCoreIndexOperations(cif *models.CoreIndexFile, txn *txn.SidetreeTxn) (*coreOperations, error) { //nolint:funlen if cif.Operations == nil { // nothing to do return &coreOperations{}, nil } @@ -644,7 +643,7 @@ func (h *OperationProvider) parseCoreIndexOperations(cif *models.CoreIndexFile, var createOps []*model.Operation for _, op := range cif.Operations.Create { - suffix, err := hashing.CalculateModelMultihash(op.SuffixData, h.MultihashAlgorithm) + suffix, err := model.GetUniqueSuffix(op.SuffixData, h.MultihashAlgorithms) if err != nil { return nil, err } diff --git a/pkg/versions/0_1/txnprovider/provider_test.go b/pkg/versions/0_1/txnprovider/provider_test.go index 532e9fb8..273af30b 100644 --- a/pkg/versions/0_1/txnprovider/provider_test.go +++ b/pkg/versions/0_1/txnprovider/provider_test.go @@ -218,7 +218,7 @@ func TestHandler_GetTxnOperations(t *testing.T) { require.NotEmpty(t, anchorString) invalid := mocks.NewMockProtocolClient().Protocol - invalid.MultihashAlgorithm = 55 + invalid.MultihashAlgorithms = []uint{55} provider := NewOperationProvider(invalid, operationparser.New(invalid), cas, cp) @@ -364,6 +364,7 @@ func TestHandler_GetCoreIndexFile(t *testing.T) { MaxCoreIndexFileSize: maxFileSize, CompressionAlgorithm: compressionAlgorithm, MaxCasURILength: 100, + MultihashAlgorithms: []uint{sha2_256}, } cas := mocks.NewMockCasClient(nil)