github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/crypto/crypto_test.go (about) 1 package crypto_test 2 3 import ( 4 "crypto/rand" 5 "fmt" 6 "testing" 7 "unicode/utf8" 8 9 "github.com/fxamacker/cbor/v2" 10 "github.com/onflow/cadence/runtime" 11 onflowCrypto "github.com/onflow/crypto" 12 "github.com/onflow/crypto/hash" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 16 "github.com/onflow/flow-go/fvm/crypto" 17 "github.com/onflow/flow-go/fvm/errors" 18 "github.com/onflow/flow-go/model/flow" 19 msig "github.com/onflow/flow-go/module/signature" 20 ) 21 22 func TestHashWithTag(t *testing.T) { 23 t.Run("tag too long", func(t *testing.T) { 24 algorithms := []hash.HashingAlgorithm{ 25 hash.SHA2_256, 26 hash.SHA2_384, 27 hash.SHA3_256, 28 hash.SHA3_384, 29 hash.Keccak_256, 30 } 31 32 okTag := [flow.DomainTagLength / 2]byte{} // tag does not exceed 32 bytes 33 longTag := [flow.DomainTagLength + 1]byte{} // tag larger that 32 bytes 34 35 for i, algorithm := range algorithms { 36 t.Run(fmt.Sprintf("algo %d: %v", i, algorithm), func(t *testing.T) { 37 _, err := crypto.HashWithTag(algorithm, string(longTag[:]), []byte("some data")) 38 require.Error(t, err) 39 }) 40 41 t.Run(fmt.Sprintf("algo %d: %v - control (tag ok)", i, algorithm), func(t *testing.T) { 42 _, err := crypto.HashWithTag(algorithm, string(okTag[:]), []byte("some data")) 43 require.NoError(t, err) 44 }) 45 } 46 }) 47 } 48 49 func TestVerifySignatureFromRuntime(t *testing.T) { 50 51 // make sure the seed length is larger than miniumum seed lengths of all signature schemes 52 seedLength := 64 53 54 correctCombinations := map[runtime.SignatureAlgorithm]map[runtime.HashAlgorithm]struct{}{ 55 56 runtime.SignatureAlgorithmBLS_BLS12_381: { 57 runtime.HashAlgorithmKMAC128_BLS_BLS12_381: {}, 58 }, 59 runtime.SignatureAlgorithmECDSA_P256: { 60 runtime.HashAlgorithmSHA2_256: {}, 61 runtime.HashAlgorithmSHA3_256: {}, 62 runtime.HashAlgorithmKECCAK_256: {}, 63 }, 64 runtime.SignatureAlgorithmECDSA_secp256k1: { 65 runtime.HashAlgorithmSHA2_256: {}, 66 runtime.HashAlgorithmSHA3_256: {}, 67 runtime.HashAlgorithmKECCAK_256: {}, 68 }, 69 } 70 71 t.Run("verify should fail on incorrect combinations", func(t *testing.T) { 72 73 signatureAlgos := []runtime.SignatureAlgorithm{ 74 runtime.SignatureAlgorithmECDSA_P256, 75 runtime.SignatureAlgorithmECDSA_secp256k1, 76 runtime.SignatureAlgorithmBLS_BLS12_381, 77 } 78 hashAlgos := []runtime.HashAlgorithm{ 79 runtime.HashAlgorithmSHA2_256, 80 runtime.HashAlgorithmSHA2_384, 81 runtime.HashAlgorithmSHA3_256, 82 runtime.HashAlgorithmSHA3_384, 83 runtime.HashAlgorithmKMAC128_BLS_BLS12_381, 84 runtime.HashAlgorithmKECCAK_256, 85 } 86 87 for _, s := range signatureAlgos { 88 for _, h := range hashAlgos { 89 t.Run(fmt.Sprintf("combination: %v, %v", s, h), func(t *testing.T) { 90 seed := make([]byte, seedLength) 91 _, err := rand.Read(seed) 92 require.NoError(t, err) 93 pk, err := onflowCrypto.GeneratePrivateKey(crypto.RuntimeToCryptoSigningAlgorithm(s), seed) 94 require.NoError(t, err) 95 96 tag := "random_tag" 97 var hasher hash.Hasher 98 if h != runtime.HashAlgorithmKMAC128_BLS_BLS12_381 { 99 hasher, err = crypto.NewPrefixedHashing(crypto.RuntimeToCryptoHashingAlgorithm(h), tag) 100 require.NoError(t, err) 101 } else { 102 hasher = msig.NewBLSHasher(tag) 103 } 104 105 signature := make([]byte, 0) 106 sig, err := pk.Sign([]byte("some data"), hasher) 107 if _, shouldBeOk := correctCombinations[s][h]; shouldBeOk { 108 require.NoError(t, err) 109 } 110 111 if sig != nil { 112 signature = sig.Bytes() 113 } 114 115 ok, err := crypto.VerifySignatureFromRuntime( 116 signature, 117 tag, 118 []byte("some data"), 119 pk.PublicKey().Encode(), 120 s, 121 h, 122 ) 123 124 if _, shouldBeOk := correctCombinations[s][h]; shouldBeOk { 125 require.NoError(t, err) 126 require.True(t, ok) 127 } else { 128 require.Error(t, err) 129 require.False(t, ok) 130 } 131 }) 132 } 133 } 134 }) 135 136 t.Run("BLS tag combinations", func(t *testing.T) { 137 cases := []struct { 138 signTag string 139 verifyTag string 140 require func(t *testing.T, sigOk bool, err error) 141 }{ 142 { 143 signTag: "random_tag", 144 verifyTag: "random_tag", 145 require: func(t *testing.T, sigOk bool, err error) { 146 require.NoError(t, err) 147 require.True(t, sigOk) 148 }, 149 }, 150 { 151 signTag: "", 152 verifyTag: "", 153 require: func(t *testing.T, sigOk bool, err error) { 154 require.NoError(t, err) 155 require.True(t, sigOk) 156 }, 157 }, { 158 signTag: "padding test", 159 verifyTag: "padding test" + string([]byte{0, 0, 0, 0, 0}), 160 require: func(t *testing.T, sigOk bool, err error) { 161 require.NoError(t, err) 162 require.False(t, sigOk) 163 }, 164 }, { 165 signTag: "valid tag", 166 verifyTag: "different valid tag", 167 require: func(t *testing.T, sigOk bool, err error) { 168 require.NoError(t, err) 169 require.False(t, sigOk) 170 }, 171 }, { 172 signTag: "a very large tag with more than thirty two bytes", 173 verifyTag: "a very large tag with more than thirty two bytes", 174 require: func(t *testing.T, sigOk bool, err error) { 175 require.NoError(t, err) 176 require.True(t, sigOk) 177 }, 178 }, 179 } 180 181 for _, c := range cases { 182 seed := make([]byte, seedLength) 183 _, err := rand.Read(seed) 184 require.NoError(t, err) 185 pk, err := onflowCrypto.GeneratePrivateKey(onflowCrypto.BLSBLS12381, seed) 186 require.NoError(t, err) 187 188 hasher := msig.NewBLSHasher(string(c.signTag)) 189 signature := make([]byte, 0) 190 sig, err := pk.Sign([]byte("some data"), hasher) 191 require.NoError(t, err) 192 193 if sig != nil { 194 signature = sig.Bytes() 195 } 196 197 ok, err := crypto.VerifySignatureFromRuntime( 198 signature, 199 string(c.verifyTag), 200 []byte("some data"), 201 pk.PublicKey().Encode(), 202 runtime.SignatureAlgorithmBLS_BLS12_381, 203 runtime.HashAlgorithmKMAC128_BLS_BLS12_381, 204 ) 205 206 c.require(t, ok, err) 207 } 208 }) 209 210 t.Run("ECDSA tag combinations", func(t *testing.T) { 211 212 cases := []struct { 213 signTag string 214 verifyTag string 215 require func(t *testing.T, sigOk bool, err error) 216 }{ 217 { 218 signTag: "random_tag", 219 verifyTag: "random_tag", 220 require: func(t *testing.T, sigOk bool, err error) { 221 require.NoError(t, err) 222 require.True(t, sigOk) 223 }, 224 }, 225 { 226 signTag: "", 227 verifyTag: "", 228 require: func(t *testing.T, sigOk bool, err error) { 229 require.NoError(t, err) 230 require.True(t, sigOk) 231 }, 232 }, { 233 signTag: "padding test", 234 verifyTag: "padding test" + string([]byte{0, 0, 0, 0, 0}), 235 require: func(t *testing.T, sigOk bool, err error) { 236 require.NoError(t, err) 237 require.True(t, sigOk) 238 }, 239 }, { 240 signTag: "valid tag", 241 verifyTag: "different valid tag", 242 require: func(t *testing.T, sigOk bool, err error) { 243 require.NoError(t, err) 244 require.False(t, sigOk) 245 }, 246 }, { 247 signTag: "valid tag", 248 verifyTag: "a very large tag with more than thirty two bytes", 249 require: func(t *testing.T, sigOk bool, err error) { 250 require.Error(t, err) 251 require.False(t, sigOk) 252 }, 253 }, 254 } 255 256 for _, c := range cases { 257 for s, hMaps := range correctCombinations { 258 if s == runtime.SignatureAlgorithmBLS_BLS12_381 { 259 // skip BLS to only cover ECDSA in this test 260 continue 261 } 262 for h := range hMaps { 263 t.Run(fmt.Sprintf("hash tag: %v, verify tag: %v [%v, %v]", c.signTag, c.verifyTag, s, h), func(t *testing.T) { 264 265 seed := make([]byte, seedLength) 266 _, err := rand.Read(seed) 267 require.NoError(t, err) 268 pk, err := onflowCrypto.GeneratePrivateKey(crypto.RuntimeToCryptoSigningAlgorithm(s), seed) 269 require.NoError(t, err) 270 271 hasher, err := crypto.NewPrefixedHashing(crypto.RuntimeToCryptoHashingAlgorithm(h), c.signTag) 272 require.NoError(t, err) 273 274 data := []byte("some data") 275 sig, err := pk.Sign(data, hasher) 276 require.NoError(t, err) 277 signature := sig.Bytes() 278 279 ok, err := crypto.VerifySignatureFromRuntime( 280 signature, 281 c.verifyTag, 282 data, 283 pk.PublicKey().Encode(), 284 s, 285 h, 286 ) 287 288 c.require(t, ok, err) 289 }) 290 } 291 } 292 } 293 }) 294 } 295 296 func TestVerifySignatureFromTransaction(t *testing.T) { 297 298 // make sure the seed length is larger than miniumum seed lengths of all signature schemes 299 seedLength := 64 300 301 correctCombinations := map[onflowCrypto.SigningAlgorithm]map[hash.HashingAlgorithm]struct{}{ 302 onflowCrypto.ECDSAP256: { 303 hash.SHA2_256: {}, 304 hash.SHA3_256: {}, 305 }, 306 onflowCrypto.ECDSASecp256k1: { 307 hash.SHA2_256: {}, 308 hash.SHA3_256: {}, 309 }, 310 } 311 312 t.Run("verify should fail on incorrect combinations", func(t *testing.T) { 313 314 signatureAlgos := []onflowCrypto.SigningAlgorithm{ 315 onflowCrypto.ECDSAP256, 316 onflowCrypto.ECDSASecp256k1, 317 onflowCrypto.BLSBLS12381, 318 } 319 hashAlgos := []hash.HashingAlgorithm{ 320 hash.SHA2_256, 321 hash.SHA2_384, 322 hash.SHA3_256, 323 hash.SHA3_384, 324 hash.KMAC128, 325 hash.Keccak_256, 326 } 327 328 for _, s := range signatureAlgos { 329 for _, h := range hashAlgos { 330 t.Run(fmt.Sprintf("combination: %v, %v", s, h), func(t *testing.T) { 331 seed := make([]byte, seedLength) 332 _, err := rand.Read(seed) 333 require.NoError(t, err) 334 sk, err := onflowCrypto.GeneratePrivateKey(s, seed) 335 require.NoError(t, err) 336 337 tag := string(flow.TransactionDomainTag[:]) 338 var hasher hash.Hasher 339 if h != hash.KMAC128 { 340 hasher, err = crypto.NewPrefixedHashing(h, tag) 341 require.NoError(t, err) 342 } else { 343 hasher = msig.NewBLSHasher(tag) 344 } 345 346 signature := make([]byte, 0) 347 data := []byte("some_data") 348 sig, err := sk.Sign(data, hasher) 349 if _, shouldBeOk := correctCombinations[s][h]; shouldBeOk { 350 require.NoError(t, err) 351 } 352 353 if sig != nil { 354 signature = sig.Bytes() 355 } 356 357 ok, err := crypto.VerifySignatureFromTransaction(signature, data, sk.PublicKey(), h) 358 359 if _, shouldBeOk := correctCombinations[s][h]; shouldBeOk { 360 require.NoError(t, err) 361 require.True(t, ok) 362 } else { 363 require.Error(t, err) 364 require.False(t, ok) 365 } 366 }) 367 } 368 } 369 }) 370 371 t.Run("tag combinations", func(t *testing.T) { 372 373 cases := []struct { 374 signTag string 375 require func(t *testing.T, sigOk bool, err error) 376 }{ 377 { 378 signTag: string(flow.TransactionDomainTag[:]), 379 require: func(t *testing.T, sigOk bool, err error) { 380 require.NoError(t, err) 381 require.True(t, sigOk) 382 }, 383 }, 384 { 385 signTag: "", 386 require: func(t *testing.T, sigOk bool, err error) { 387 require.NoError(t, err) 388 require.False(t, sigOk) 389 }, 390 }, { 391 signTag: "random_tag", 392 require: func(t *testing.T, sigOk bool, err error) { 393 require.NoError(t, err) 394 require.False(t, sigOk) 395 }, 396 }, 397 } 398 399 for _, c := range cases { 400 for s, hMaps := range correctCombinations { 401 for h := range hMaps { 402 t.Run(fmt.Sprintf("sign tag: %v [%v, %v]", c.signTag, s, h), func(t *testing.T) { 403 seed := make([]byte, seedLength) 404 _, err := rand.Read(seed) 405 require.NoError(t, err) 406 sk, err := onflowCrypto.GeneratePrivateKey(s, seed) 407 require.NoError(t, err) 408 409 hasher, err := crypto.NewPrefixedHashing(h, c.signTag) 410 require.NoError(t, err) 411 412 data := []byte("some data") 413 sig, err := sk.Sign(data, hasher) 414 require.NoError(t, err) 415 signature := sig.Bytes() 416 417 ok, err := crypto.VerifySignatureFromTransaction(signature, data, sk.PublicKey(), h) 418 c.require(t, ok, err) 419 }) 420 } 421 } 422 } 423 }) 424 } 425 426 func TestValidatePublicKey(t *testing.T) { 427 428 validPublicKey := func(t *testing.T, s runtime.SignatureAlgorithm) []byte { 429 seed := make([]byte, onflowCrypto.KeyGenSeedMinLen) 430 _, err := rand.Read(seed) 431 require.NoError(t, err) 432 sk, err := onflowCrypto.GeneratePrivateKey(crypto.RuntimeToCryptoSigningAlgorithm(s), seed) 433 require.NoError(t, err) 434 return sk.PublicKey().Encode() 435 } 436 437 t.Run("Unknown algorithm should return false", func(t *testing.T) { 438 err := crypto.ValidatePublicKey(runtime.SignatureAlgorithmUnknown, validPublicKey(t, runtime.SignatureAlgorithmECDSA_P256)) 439 require.Error(t, err) 440 }) 441 442 t.Run("valid public key should return true", func(t *testing.T) { 443 signatureAlgos := []runtime.SignatureAlgorithm{ 444 runtime.SignatureAlgorithmECDSA_P256, 445 runtime.SignatureAlgorithmECDSA_secp256k1, 446 runtime.SignatureAlgorithmBLS_BLS12_381, 447 } 448 for i, s := range signatureAlgos { 449 t.Run(fmt.Sprintf("case %v: %v", i, s), func(t *testing.T) { 450 err := crypto.ValidatePublicKey(s, validPublicKey(t, s)) 451 require.NoError(t, err) 452 }) 453 } 454 }) 455 456 t.Run("invalid public key should return false", func(t *testing.T) { 457 signatureAlgos := []runtime.SignatureAlgorithm{ 458 runtime.SignatureAlgorithmECDSA_P256, 459 runtime.SignatureAlgorithmECDSA_secp256k1, 460 runtime.SignatureAlgorithmBLS_BLS12_381, 461 } 462 for i, s := range signatureAlgos { 463 464 t.Run(fmt.Sprintf("case %v: %v", i, s), func(t *testing.T) { 465 key := validPublicKey(t, s) 466 // This may cause flakiness depending on the public key 467 // deserialization scheme used!! 468 key[0] ^= 1 // alter one bit of the valid key 469 err := crypto.ValidatePublicKey(s, key) 470 require.Errorf(t, err, "key is %#x", key) 471 }) 472 } 473 }) 474 } 475 476 func TestHashingAlgorithmConversion(t *testing.T) { 477 hashingAlgoMapping := map[runtime.HashAlgorithm]hash.HashingAlgorithm{ 478 runtime.HashAlgorithmSHA2_256: hash.SHA2_256, 479 runtime.HashAlgorithmSHA3_256: hash.SHA3_256, 480 runtime.HashAlgorithmSHA2_384: hash.SHA2_384, 481 runtime.HashAlgorithmSHA3_384: hash.SHA3_384, 482 runtime.HashAlgorithmKMAC128_BLS_BLS12_381: hash.KMAC128, 483 runtime.HashAlgorithmKECCAK_256: hash.Keccak_256, 484 } 485 486 for runtimeAlgo, cryptoAlgo := range hashingAlgoMapping { 487 assert.Equal(t, cryptoAlgo, crypto.RuntimeToCryptoHashingAlgorithm(runtimeAlgo)) 488 assert.Equal(t, runtimeAlgo, crypto.CryptoToRuntimeHashingAlgorithm(cryptoAlgo)) 489 } 490 } 491 492 func TestSigningAlgorithmConversion(t *testing.T) { 493 signingAlgoMapping := map[runtime.SignatureAlgorithm]onflowCrypto.SigningAlgorithm{ 494 runtime.SignatureAlgorithmECDSA_P256: onflowCrypto.ECDSAP256, 495 runtime.SignatureAlgorithmECDSA_secp256k1: onflowCrypto.ECDSASecp256k1, 496 runtime.SignatureAlgorithmBLS_BLS12_381: onflowCrypto.BLSBLS12381, 497 } 498 499 for runtimeAlgo, cryptoAlgo := range signingAlgoMapping { 500 assert.Equal(t, cryptoAlgo, crypto.RuntimeToCryptoSigningAlgorithm(runtimeAlgo)) 501 assert.Equal(t, runtimeAlgo, crypto.CryptoToRuntimeSigningAlgorithm(cryptoAlgo)) 502 } 503 } 504 505 func TestVerifySignatureFromRuntime_error_handling_produces_valid_utf8_for_invalid_sign_algo(t *testing.T) { 506 507 invalidSignatureAlgo := runtime.SignatureAlgorithm(164) 508 509 _, err := crypto.VerifySignatureFromRuntime( 510 nil, "", nil, nil, invalidSignatureAlgo, 0, 511 ) 512 513 require.True(t, errors.IsValueError(err)) 514 515 require.Contains(t, err.Error(), fmt.Sprintf("%d", invalidSignatureAlgo)) 516 517 errorString := err.Error() 518 assert.True(t, utf8.ValidString(errorString)) 519 520 // check if they can encoded and decoded using CBOR 521 marshalledBytes, err := cbor.Marshal(errorString) 522 require.NoError(t, err) 523 524 var unmarshalledString string 525 526 err = cbor.Unmarshal(marshalledBytes, &unmarshalledString) 527 require.NoError(t, err) 528 529 require.Equal(t, errorString, unmarshalledString) 530 } 531 532 func TestVerifySignatureFromRuntime_error_handling_produces_valid_utf8_for_invalid_hash_algo(t *testing.T) { 533 534 invalidHashAlgo := runtime.HashAlgorithm(164) 535 536 _, err := crypto.VerifySignatureFromRuntime( 537 nil, "", nil, nil, runtime.SignatureAlgorithmECDSA_P256, invalidHashAlgo, 538 ) 539 540 require.True(t, errors.IsValueError(err)) 541 542 require.Contains(t, err.Error(), fmt.Sprintf("%d", invalidHashAlgo)) 543 544 errorString := err.Error() 545 assert.True(t, utf8.ValidString(errorString)) 546 547 // check if they can encoded and decoded using CBOR 548 marshalledBytes, err := cbor.Marshal(errorString) 549 require.NoError(t, err) 550 551 var unmarshalledString string 552 553 err = cbor.Unmarshal(marshalledBytes, &unmarshalledString) 554 require.NoError(t, err) 555 556 require.Equal(t, errorString, unmarshalledString) 557 } 558 559 func TestVerifySignatureFromRuntime_error_handling_produces_valid_utf8_for_invalid_public_key(t *testing.T) { 560 561 invalidPublicKey := []byte{0xc3, 0x28} // some invalid UTF8 562 563 _, err := crypto.VerifySignatureFromRuntime( 564 nil, "random_tag", nil, invalidPublicKey, runtime.SignatureAlgorithmECDSA_P256, runtime.HashAlgorithmSHA2_256, 565 ) 566 567 require.True(t, errors.IsValueError(err)) 568 errorString := err.Error() 569 570 require.Contains(t, errorString, fmt.Sprintf("%x", invalidPublicKey)) 571 572 assert.True(t, utf8.ValidString(errorString)) 573 574 // check if they can encoded and decoded using CBOR 575 marshalledBytes, err := cbor.Marshal(errorString) 576 require.NoError(t, err) 577 578 var unmarshalledString string 579 580 err = cbor.Unmarshal(marshalledBytes, &unmarshalledString) 581 require.NoError(t, err) 582 583 require.Equal(t, errorString, unmarshalledString) 584 }