From e058dc7d355c7dd25ff4cbf2add5a9f5977cbe32 Mon Sep 17 00:00:00 2001 From: pleasew8t Date: Tue, 29 Oct 2024 15:11:11 +0200 Subject: [PATCH 1/4] node: add amazon kms and benchmark signers --- node/go.mod | 14 +- node/go.sum | 35 +++- node/pkg/guardiansigner/amazonkms.go | 186 ++++++++++++++++++ node/pkg/guardiansigner/benchmarksigner.go | 59 ++++++ node/pkg/guardiansigner/guardiansigner.go | 12 ++ .../pkg/guardiansigner/guardiansigner_test.go | 2 + 6 files changed, 302 insertions(+), 6 deletions(-) create mode 100644 node/pkg/guardiansigner/amazonkms.go create mode 100644 node/pkg/guardiansigner/benchmarksigner.go diff --git a/node/go.mod b/node/go.mod index 21f7f94c07..23cce59d23 100644 --- a/node/go.mod +++ b/node/go.mod @@ -46,6 +46,9 @@ require ( require ( github.com/CosmWasm/wasmd v0.30.0 github.com/algorand/go-algorand-sdk v1.23.0 + github.com/aws/aws-sdk-go v1.55.5 + github.com/aws/aws-sdk-go-v2/config v1.15.1 + github.com/aws/aws-sdk-go-v2/service/kms v1.37.2 github.com/benbjohnson/clock v1.3.5 github.com/blendle/zapdriver v1.3.1 github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce @@ -84,7 +87,16 @@ require ( github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go v1.44.187 // indirect + github.com/aws/aws-sdk-go-v2 v1.32.2 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.11.0 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.8 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.11.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.16.1 // indirect + github.com/aws/smithy-go v1.22.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/btcsuite/btcd v0.22.1 // indirect diff --git a/node/go.sum b/node/go.sum index 82ce15f2a7..eead63729a 100644 --- a/node/go.sum +++ b/node/go.sum @@ -692,21 +692,49 @@ github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2z github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.44.187 h1:D5CsRomPnlwDHJCanL2mtaLIcbhjiWxNh5j8zvaWdJA= -github.com/aws/aws-sdk-go v1.44.187/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2 v1.16.0/go.mod h1:lJYcuZZEHWNIb6ugJjbQY1fykdoobWbOS7kJYb4APoI= +github.com/aws/aws-sdk-go-v2 v1.32.2 h1:AkNLZEyYMLnx/Q/mSKkcMqwNFXMAvFto9bNsHqcTduI= +github.com/aws/aws-sdk-go-v2 v1.32.2/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= +github.com/aws/aws-sdk-go-v2/config v1.15.1 h1:hTIZFepYESYyowQUBo47lu69WSxsYqGUILY9Nu8+7pY= +github.com/aws/aws-sdk-go-v2/config v1.15.1/go.mod h1:MZHGbuW2WnqIOQQBKu2ZkhTjuutZSTnn56TDq4QyydE= github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= +github.com/aws/aws-sdk-go-v2/credentials v1.11.0 h1:gc4Uhs80s60nmLon5Z4JXWinX2BkAGT0YROoUT8h8U4= +github.com/aws/aws-sdk-go-v2/credentials v1.11.0/go.mod h1:EdV1ZFgtZ4XM5RDHWcRWK8H+xW5duNVBqWj2oLu7tRo= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.1 h1:F9Je1nq5YXfMOv6451NHvMf6U0iTWeMnsG0MMIQoUmk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.1/go.mod h1:Yph0XsTbQ5GGZ2+mO1a03P/SO9fdX3t1nejIp2tq79g= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.7/go.mod h1:oB9nZcxH1cGq7NPGurVJwxrO2vmJ9mmEBayCwcAlmT8= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 h1:UAsR3xA31QGf79WzpG/ixT9FZvQlh5HY1NRqSHBNOCk= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21/go.mod h1:JNr43NFf5L9YaG3eKTm7HQzls9J+A9YYcGI5Quh1r2Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.1/go.mod h1:K4vz7lRYCyLYpYAMCLObODahFgARdD3YVa0MvQte9Co= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 h1:6jZVETqmYCadGFvrYEQfC5fAQmlo80CeL5psbno6r0s= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21/go.mod h1:1SR0GbLlnN3QUmYaflZNiH1ql+1qrSiB2vwcJ+4UM60= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.8 h1:adr3PfiggFtqgFofAMUFCtdvwzpf3QxPES4ezK4M3iI= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.8/go.mod h1:wLbQYt36AJqaRZUQiCNXzbtkNigyPfKHrotHuIDiCy8= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.1 h1:B/SPX7J+Y0Yrcjv60Nhbh1gC2uBN47SfN8JYre6Mp4M= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.1/go.mod h1:2Hhr9Eh1gJzDatwACX/ozAZ/ljq5vzvPRu5cdu25tzc= +github.com/aws/aws-sdk-go-v2/service/kms v1.37.2 h1:tfBABi5R6aSZlhgTWHxL+opYUDOnIGoNcJLwVYv0jLM= +github.com/aws/aws-sdk-go-v2/service/kms v1.37.2/go.mod h1:dZYFcQwuoh+cLOlFnZItijZptmyDhRIkOKWFO1CfzV8= github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.1 h1:DyHctRsJIAWIvom1Itb4T84D2jwpIu+KIi3d0SFaswg= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.1/go.mod h1:CvFTucADIx7U/M44vjLs/ZttpQHdpxwK+62+dUGhDeY= github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= +github.com/aws/aws-sdk-go-v2/service/sts v1.16.1 h1:xsOtPAvHqhvQvBza5ohaUcfq1LceH2lZKMUGZJKiZiM= +github.com/aws/aws-sdk-go-v2/service/sts v1.16.1/go.mod h1:Aq2/Qggh2oemSfyHH+EO4UBbgWG6zFCXLHYI4ILTY7w= github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/aws/smithy-go v1.11.1/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= +github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM= +github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -3524,7 +3552,6 @@ golang.org/x/net v0.0.0-20220726230323-06994584191e/go.mod h1:AaygXjzTFtRAg2ttMY golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= @@ -3760,7 +3787,6 @@ golang.org/x/sys v0.0.0-20220727055044-e65921a090b8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -3776,7 +3802,6 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= diff --git a/node/pkg/guardiansigner/amazonkms.go b/node/pkg/guardiansigner/amazonkms.go new file mode 100644 index 0000000000..88f8ca94bf --- /dev/null +++ b/node/pkg/guardiansigner/amazonkms.go @@ -0,0 +1,186 @@ +package guardiansigner + +import ( + "bytes" + "context" + "crypto/ecdsa" + "encoding/asn1" + "errors" + "fmt" + "math/big" + + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" + kms_types "github.com/aws/aws-sdk-go-v2/service/kms/types" + "github.com/aws/aws-sdk-go/aws" + ethcrypto "github.com/ethereum/go-ethereum/crypto" +) + +var ( + secp256k1N = ethcrypto.S256().Params().N + secp256k1HalfN = new(big.Int).Div(secp256k1N, big.NewInt(2)) +) + +type asn1EcSig struct { + R asn1.RawValue + S asn1.RawValue +} + +type asn1EcPublicKey struct { + EcPublicKeyInfo asn1EcPublicKeyInfo + PublicKey asn1.BitString +} + +type asn1EcPublicKeyInfo struct { + Algorithm asn1.ObjectIdentifier + Parameters asn1.ObjectIdentifier +} + +type AmazonKms struct { + KeyId string + Region string + svc *kms.Client +} + +func NewAmazonKmsSigner(unsafeDevMode bool, keyPath string) (*AmazonKms, error) { + amazonKmsSigner := AmazonKms{ + KeyId: keyPath, + } + + cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("eu-north-1")) + if err != nil { + return nil, errors.New("failed to load default config") + } + + amazonKmsSigner.svc = kms.NewFromConfig(cfg) + + return &amazonKmsSigner, nil +} + +func (a *AmazonKms) Sign(hash []byte) (signature []byte, err error) { + + // request signing + res, err := a.svc.Sign(context.TODO(), &kms.SignInput{ + KeyId: aws.String(a.KeyId), + Message: hash, + SigningAlgorithm: kms_types.SigningAlgorithmSpecEcdsaSha256, + MessageType: kms_types.MessageTypeDigest, + }) + + if err != nil { + return nil, fmt.Errorf("Signing failed: %w", err) + } + + // decode r and s values + r, s := derSignatureToRS(res.Signature) + + // if s is greater than secp256k1HalfN, we need to substract secp256k1N from it + sBigInt := new(big.Int).SetBytes(s) + if sBigInt.Cmp(secp256k1HalfN) > 0 { + s = new(big.Int).Sub(secp256k1N, sBigInt).Bytes() + } + + // r and s need to be 32 bytes in size + r = adjustBufferSize(r) + s = adjustBufferSize(s) + + // AWS KMS does not provide the recovery id. But that doesn't matter too much, since we can + // attempt recovery id's 0 and 1, and in the process ensure that the signature is valid. + expectedPublicKey := a.PublicKey() + signature = append(r, s...) + + // try recovery id 0 + ecSigWithRecid := append(signature, []byte{0}...) + pubkey, err := ethcrypto.SigToPub(hash[:], ecSigWithRecid) + + if bytes.Equal(ethcrypto.CompressPubkey(pubkey), ethcrypto.CompressPubkey(&expectedPublicKey)) { + return ecSigWithRecid, nil + } + + ecSigWithRecid = append(signature, []byte{1}...) + pubkey, err = ethcrypto.SigToPub(hash[:], ecSigWithRecid) + + // try recovery id 1 + if bytes.Equal(ethcrypto.CompressPubkey(pubkey), ethcrypto.CompressPubkey(&expectedPublicKey)) { + return ecSigWithRecid, nil + } + + return nil, fmt.Errorf("Failed to generate signature") +} + +func (a *AmazonKms) PublicKey() ecdsa.PublicKey { + pubKeyOutput, _ := a.svc.GetPublicKey(context.TODO(), &kms.GetPublicKeyInput{ + KeyId: aws.String(a.KeyId), + }) + + // if err != nil { + // return nil, fmt.Errorf("error getting public key: %w", err) + // } + + var asn1Pubkey asn1EcPublicKey + _, _ = asn1.Unmarshal(pubKeyOutput.PublicKey, &asn1Pubkey) + // if err != nil { + // return nil, fmt.Errorf("error during unmarshalling: %w", err) + // } + + // if len(asn1Pubkey.PublicKey.Bytes) != 65 { + // return nil, fmt.Errorf("wut %w", err) + // } + + ecdsaPubkey := ecdsa.PublicKey{ + X: new(big.Int).SetBytes(asn1Pubkey.PublicKey.Bytes[1 : 1+32]), + Y: new(big.Int).SetBytes(asn1Pubkey.PublicKey.Bytes[1+32:]), + } + + return ecdsaPubkey +} + +func (a *AmazonKms) Verify(sig []byte, hash []byte) (bool, error) { + return true, nil +} + +// https://bitcoin.stackexchange.com/questions/92680/what-are-the-der-signature-and-sec-format +// 1. 0x30 byte: header byte to indicate compound structure +// 2. one byte to encode the length of the following data +// 3. 0x02: header byte indicating an integer +// 4. one byte to encode the length of the following r value +// 5. the r value as a big-endian integer +// 6. 0x02: header byte indicating an integer +// 7. one byte to encode the length of the following s value +// 8. the s value as a big-endian integer +func derSignatureToRS(signature []byte) (rBytes []byte, sBytes []byte) { + // // read r-length, followed by r + // rLen := uint(signature[3]) + // rBytes = signature[4 : 4+rLen] + + // // read s-length, followed by s + // sLen := uint(signature[4+rLen+1]) + // sBytes = signature[4+rLen+2 : 4+rLen+2+sLen] + + var sigAsn1 asn1EcSig + _, err := asn1.Unmarshal(signature, &sigAsn1) + + if err != nil { + panic(err) + } + + return sigAsn1.R.Bytes, sigAsn1.S.Bytes + // return rBytes, sBytes +} + +func adjustBufferSize(b []byte) []byte { + length := len(b) + + if length == 32 { + return b + } + + if length > 32 { + return b[length-32:] + } + + tmp := make([]byte, 32) + copy(tmp[32-length:], b) + + return tmp +} diff --git a/node/pkg/guardiansigner/benchmarksigner.go b/node/pkg/guardiansigner/benchmarksigner.go new file mode 100644 index 0000000000..d7478524d4 --- /dev/null +++ b/node/pkg/guardiansigner/benchmarksigner.go @@ -0,0 +1,59 @@ +package guardiansigner + +import ( + "crypto/ecdsa" + "fmt" + "time" +) + +type BenchmarkSigner struct { + innerSigner GuardianSigner +} + +func NewBenchmarkSigner(unsafeDevMode bool, signerKeyPath string) (*BenchmarkSigner, error) { + innerSigner, err := NewGuardianSignerFromUri(signerKeyPath, unsafeDevMode) + + if err != nil { + return nil, fmt.Errorf("failed to create benchmark signer: %w", err) + } + + return &BenchmarkSigner{ + innerSigner: innerSigner, + }, nil +} + +func (b *BenchmarkSigner) Sign(hash []byte) ([]byte, error) { + + start := time.Now() + + sig, err := b.innerSigner.Sign(hash) + + duration := time.Since(start) + fmt.Printf("Signing execution time: %v\n", duration) + + return sig, err +} + +func (b *BenchmarkSigner) PublicKey() ecdsa.PublicKey { + + start := time.Now() + + pubKey := b.innerSigner.PublicKey() + + duration := time.Since(start) + fmt.Printf("Public key retrieval time: %v\n", duration) + + return pubKey +} + +func (b *BenchmarkSigner) Verify(sig []byte, hash []byte) (bool, error) { + + start := time.Now() + + valid, err := b.innerSigner.Verify(sig, hash) + + duration := time.Since(start) + fmt.Printf("Signature verification time: %v\n", duration) + + return valid, err +} diff --git a/node/pkg/guardiansigner/guardiansigner.go b/node/pkg/guardiansigner/guardiansigner.go index 057f9c03bf..d0e9ccba94 100644 --- a/node/pkg/guardiansigner/guardiansigner.go +++ b/node/pkg/guardiansigner/guardiansigner.go @@ -14,6 +14,10 @@ const ( InvalidSignerType SignerType = iota // file:// FileSignerType + // amazonkms:// + AmazonKmsSignerType + // benchmark://:// + BenchmarkSignerType ) // GuardianSigner interface @@ -40,6 +44,10 @@ func NewGuardianSignerFromUri(signerUri string, unsafeDevMode bool) (GuardianSig switch signerType { case FileSignerType: return NewFileSigner(unsafeDevMode, signerKeyConfig) + case AmazonKmsSignerType: + return NewAmazonKmsSigner(unsafeDevMode, signerKeyConfig) + case BenchmarkSignerType: + return NewBenchmarkSigner(unsafeDevMode, signerKeyConfig) default: return nil, errors.New("unsupported guardian signer type") } @@ -62,6 +70,10 @@ func ParseSignerUri(signerUri string) (signerType SignerType, signerKeyConfig st switch typeStr { case "file": return FileSignerType, keyConfig, nil + case "amazonkms": + return AmazonKmsSignerType, keyConfig, nil + case "benchmark": + return BenchmarkSignerType, keyConfig, nil default: return InvalidSignerType, "", fmt.Errorf("unsupported guardian signer type: %s", typeStr) } diff --git a/node/pkg/guardiansigner/guardiansigner_test.go b/node/pkg/guardiansigner/guardiansigner_test.go index 5e660357ed..a927afdb66 100644 --- a/node/pkg/guardiansigner/guardiansigner_test.go +++ b/node/pkg/guardiansigner/guardiansigner_test.go @@ -22,6 +22,8 @@ func TestParseSignerUri(t *testing.T) { {label: "FileUriNoSchemeSeparator", path: "filewhatever", expectedType: InvalidSignerType}, {label: "FileUriMultipleSchemeSeparators", path: "file://testing://this://", expectedType: FileSignerType}, {label: "FileUriTraversal", path: "file://../../../file", expectedType: FileSignerType}, + // Amazon KMS + {label: "AmazonKmsURI", path: "amazonkms://some-arn", expectedType: AmazonKmsSignerType}, } for _, testcase := range tests { From a210ae6fd8f35ec4ce5255a16be1164234025bdc Mon Sep 17 00:00:00 2001 From: pleasew8t Date: Tue, 29 Oct 2024 15:28:41 +0200 Subject: [PATCH 2/4] cleanup uneccessary comments --- node/pkg/guardiansigner/amazonkms.go | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/node/pkg/guardiansigner/amazonkms.go b/node/pkg/guardiansigner/amazonkms.go index 88f8ca94bf..c7167de28a 100644 --- a/node/pkg/guardiansigner/amazonkms.go +++ b/node/pkg/guardiansigner/amazonkms.go @@ -113,19 +113,8 @@ func (a *AmazonKms) PublicKey() ecdsa.PublicKey { KeyId: aws.String(a.KeyId), }) - // if err != nil { - // return nil, fmt.Errorf("error getting public key: %w", err) - // } - var asn1Pubkey asn1EcPublicKey _, _ = asn1.Unmarshal(pubKeyOutput.PublicKey, &asn1Pubkey) - // if err != nil { - // return nil, fmt.Errorf("error during unmarshalling: %w", err) - // } - - // if len(asn1Pubkey.PublicKey.Bytes) != 65 { - // return nil, fmt.Errorf("wut %w", err) - // } ecdsaPubkey := ecdsa.PublicKey{ X: new(big.Int).SetBytes(asn1Pubkey.PublicKey.Bytes[1 : 1+32]), @@ -149,14 +138,6 @@ func (a *AmazonKms) Verify(sig []byte, hash []byte) (bool, error) { // 7. one byte to encode the length of the following s value // 8. the s value as a big-endian integer func derSignatureToRS(signature []byte) (rBytes []byte, sBytes []byte) { - // // read r-length, followed by r - // rLen := uint(signature[3]) - // rBytes = signature[4 : 4+rLen] - - // // read s-length, followed by s - // sLen := uint(signature[4+rLen+1]) - // sBytes = signature[4+rLen+2 : 4+rLen+2+sLen] - var sigAsn1 asn1EcSig _, err := asn1.Unmarshal(signature, &sigAsn1) From 8e8517d4ed047c72ae86c3d13e25a7ddecc2804c Mon Sep 17 00:00:00 2001 From: pleasew8t Date: Wed, 6 Nov 2024 20:19:19 +0200 Subject: [PATCH 3/4] add contexts to GuardianSigner interface --- node/cmd/guardiand/adminclient.go | 6 +- node/cmd/guardiand/node.go | 18 +-- node/go.mod | 26 ++-- node/go.sum | 30 +++++ node/hack/accountant/send_obs.go | 2 +- node/pkg/accountant/accountant.go | 2 +- node/pkg/accountant/submit_obs.go | 2 +- node/pkg/adminrpc/adminserver.go | 2 +- node/pkg/adminrpc/adminserver_test.go | 6 +- node/pkg/governor/governor_monitoring.go | 5 +- node/pkg/guardiansigner/amazonkms.go | 127 +++++++++++++----- node/pkg/guardiansigner/benchmarksigner.go | 17 +-- node/pkg/guardiansigner/filesigner.go | 9 +- node/pkg/guardiansigner/generatedsigner.go | 7 +- node/pkg/guardiansigner/guardiansigner.go | 15 ++- .../pkg/guardiansigner/guardiansigner_test.go | 64 +++++++-- node/pkg/node/adminServiceRunnable.go | 2 +- node/pkg/node/node_test.go | 2 +- node/pkg/p2p/ccq_p2p.go | 2 +- node/pkg/p2p/p2p.go | 15 ++- node/pkg/p2p/p2p_test.go | 7 +- node/pkg/p2p/watermark_test.go | 2 +- node/pkg/processor/benchmark_test.go | 8 +- node/pkg/processor/message.go | 3 +- node/pkg/processor/processor.go | 2 +- 25 files changed, 264 insertions(+), 117 deletions(-) diff --git a/node/cmd/guardiand/adminclient.go b/node/cmd/guardiand/adminclient.go index ee087431aa..34c9b65eef 100644 --- a/node/cmd/guardiand/adminclient.go +++ b/node/cmd/guardiand/adminclient.go @@ -236,13 +236,15 @@ func getPublicRPCServiceClient(ctx context.Context, addr string) (*grpc.ClientCo } func runSignWormchainValidatorAddress(cmd *cobra.Command, args []string) error { + ctx := context.Background() + guardianSignerUri := args[0] wormchainAddress := args[1] if !strings.HasPrefix(wormchainAddress, "wormhole") || strings.HasPrefix(wormchainAddress, "wormholeval") { return errors.New("must provide a bech32 address that has 'wormhole' prefix") } - guardianSigner, err := guardiansigner.NewGuardianSignerFromUri(guardianSignerUri, *unsafeDevnetMode) + guardianSigner, err := guardiansigner.NewGuardianSignerFromUri(ctx, guardianSignerUri, *unsafeDevnetMode) if err != nil { return fmt.Errorf("failed to create new guardian signer from uri: %w", err) } @@ -254,7 +256,7 @@ func runSignWormchainValidatorAddress(cmd *cobra.Command, args []string) error { // Hash and sign address addrHash := crypto.Keccak256Hash(sdk.SignedWormchainAddressPrefix, addr) - sig, err := guardianSigner.Sign(addrHash.Bytes()) + sig, err := guardianSigner.Sign(ctx, addrHash.Bytes()) if err != nil { return fmt.Errorf("failed to sign wormchain address: %w", err) } diff --git a/node/cmd/guardiand/node.go b/node/cmd/guardiand/node.go index c1c4581a6c..1187d7918b 100644 --- a/node/cmd/guardiand/node.go +++ b/node/cmd/guardiand/node.go @@ -689,14 +689,18 @@ func runNode(cmd *cobra.Command, args []string) { } } + // Node's main lifecycle context. + rootCtx, rootCtxCancel = context.WithCancel(context.Background()) + defer rootCtxCancel() + // Create the Guardian Signer - guardianSigner, err := guardiansigner.NewGuardianSignerFromUri(*guardianSignerUri, env == common.UnsafeDevNet) + guardianSigner, err := guardiansigner.NewGuardianSignerFromUri(rootCtx, *guardianSignerUri, env == common.UnsafeDevNet) if err != nil { logger.Fatal("failed to create a new guardian signer", zap.Error(err)) } - logger.Info("Loaded guardian key", zap.String( - "address", ethcrypto.PubkeyToAddress(guardianSigner.PublicKey()).String())) + logger.Info("Created the guardian signer", zap.String( + "address", ethcrypto.PubkeyToAddress(guardianSigner.PublicKey(rootCtx)).String())) // Load p2p private key var p2pKey libp2p_crypto.PrivKey @@ -752,7 +756,7 @@ func runNode(cmd *cobra.Command, args []string) { labels := map[string]string{ "node_name": *nodeName, "node_key": peerID.String(), - "guardian_addr": ethcrypto.PubkeyToAddress(guardianSigner.PublicKey()).String(), + "guardian_addr": ethcrypto.PubkeyToAddress(guardianSigner.PublicKey(rootCtx)).String(), "network": *p2pNetworkID, "version": version.Version(), } @@ -957,10 +961,6 @@ func runNode(cmd *cobra.Command, args []string) { rpcMap[ibcChain.String()] = "IBC" } - // Node's main lifecycle context. - rootCtx, rootCtxCancel = context.WithCancel(context.Background()) - defer rootCtxCancel() - // Handle SIGTERM sigterm := make(chan os.Signal, 1) signal.Notify(sigterm, syscall.SIGTERM) @@ -1097,7 +1097,7 @@ func runNode(cmd *cobra.Command, args []string) { info.PromRemoteURL = *promRemoteURL info.Labels = map[string]string{ "node_name": *nodeName, - "guardian_addr": ethcrypto.PubkeyToAddress(guardianSigner.PublicKey()).String(), + "guardian_addr": ethcrypto.PubkeyToAddress(guardianSigner.PublicKey(rootCtx)).String(), "network": *p2pNetworkID, "version": version.Version(), "product": "wormhole", diff --git a/node/go.mod b/node/go.mod index 23cce59d23..bd434415fd 100644 --- a/node/go.mod +++ b/node/go.mod @@ -47,8 +47,8 @@ require ( github.com/CosmWasm/wasmd v0.30.0 github.com/algorand/go-algorand-sdk v1.23.0 github.com/aws/aws-sdk-go v1.55.5 - github.com/aws/aws-sdk-go-v2/config v1.15.1 - github.com/aws/aws-sdk-go-v2/service/kms v1.37.2 + github.com/aws/aws-sdk-go-v2/config v1.28.1 + github.com/aws/aws-sdk-go-v2/service/kms v1.37.3 github.com/benbjohnson/clock v1.3.5 github.com/blendle/zapdriver v1.3.1 github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce @@ -87,15 +87,19 @@ require ( github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2 v1.32.2 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.11.0 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.8 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.11.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.16.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.32.3 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.42 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.42.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 // indirect + github.com/aws/aws-sdk-go-v2/service/route53 v1.46.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 // indirect github.com/aws/smithy-go v1.22.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect diff --git a/node/go.sum b/node/go.sum index eead63729a..a6e4376b68 100644 --- a/node/go.sum +++ b/node/go.sum @@ -700,36 +700,66 @@ github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVj github.com/aws/aws-sdk-go-v2 v1.16.0/go.mod h1:lJYcuZZEHWNIb6ugJjbQY1fykdoobWbOS7kJYb4APoI= github.com/aws/aws-sdk-go-v2 v1.32.2 h1:AkNLZEyYMLnx/Q/mSKkcMqwNFXMAvFto9bNsHqcTduI= github.com/aws/aws-sdk-go-v2 v1.32.2/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= +github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk= +github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= github.com/aws/aws-sdk-go-v2/config v1.15.1 h1:hTIZFepYESYyowQUBo47lu69WSxsYqGUILY9Nu8+7pY= github.com/aws/aws-sdk-go-v2/config v1.15.1/go.mod h1:MZHGbuW2WnqIOQQBKu2ZkhTjuutZSTnn56TDq4QyydE= +github.com/aws/aws-sdk-go-v2/config v1.28.1 h1:oxIvOUXy8x0U3fR//0eq+RdCKimWI900+SV+10xsCBw= +github.com/aws/aws-sdk-go-v2/config v1.28.1/go.mod h1:bRQcttQJiARbd5JZxw6wG0yIK3eLeSCPdg6uqmmlIiI= github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= github.com/aws/aws-sdk-go-v2/credentials v1.11.0 h1:gc4Uhs80s60nmLon5Z4JXWinX2BkAGT0YROoUT8h8U4= github.com/aws/aws-sdk-go-v2/credentials v1.11.0/go.mod h1:EdV1ZFgtZ4XM5RDHWcRWK8H+xW5duNVBqWj2oLu7tRo= +github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM= +github.com/aws/aws-sdk-go-v2/credentials v1.17.42/go.mod h1:FwZBfU530dJ26rv9saAbxa9Ej3eF/AK0OAY86k13n4M= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.1 h1:F9Je1nq5YXfMOv6451NHvMf6U0iTWeMnsG0MMIQoUmk= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.1/go.mod h1:Yph0XsTbQ5GGZ2+mO1a03P/SO9fdX3t1nejIp2tq79g= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 h1:68jFVtt3NulEzojFesM/WVarlFpCaXLKaBxDpzkQ9OQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18/go.mod h1:Fjnn5jQVIo6VyedMc0/EhPpfNlPl7dHV916O6B+49aE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.7/go.mod h1:oB9nZcxH1cGq7NPGurVJwxrO2vmJ9mmEBayCwcAlmT8= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 h1:UAsR3xA31QGf79WzpG/ixT9FZvQlh5HY1NRqSHBNOCk= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21/go.mod h1:JNr43NFf5L9YaG3eKTm7HQzls9J+A9YYcGI5Quh1r2Y= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.1/go.mod h1:K4vz7lRYCyLYpYAMCLObODahFgARdD3YVa0MvQte9Co= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 h1:6jZVETqmYCadGFvrYEQfC5fAQmlo80CeL5psbno6r0s= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21/go.mod h1:1SR0GbLlnN3QUmYaflZNiH1ql+1qrSiB2vwcJ+4UM60= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.8 h1:adr3PfiggFtqgFofAMUFCtdvwzpf3QxPES4ezK4M3iI= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.8/go.mod h1:wLbQYt36AJqaRZUQiCNXzbtkNigyPfKHrotHuIDiCy8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.42.3 h1:C6oS3hSFIB1ydz3dhgkZ0HyzWV41qVjNxS/mA0AGLMQ= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.42.3/go.mod h1:OXYzq1k1XwhwghGdHASEDeFr0Ij8dyFRaIy6w0yrIms= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.1 h1:B/SPX7J+Y0Yrcjv60Nhbh1gC2uBN47SfN8JYre6Mp4M= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.1/go.mod h1:2Hhr9Eh1gJzDatwACX/ozAZ/ljq5vzvPRu5cdu25tzc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 h1:qcxX0JYlgWH3hpPUnd6U0ikcl6LLA9sLkXE2w1fpMvY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3/go.mod h1:cLSNEmI45soc+Ef8K/L+8sEA3A3pYFEYf5B5UI+6bH4= github.com/aws/aws-sdk-go-v2/service/kms v1.37.2 h1:tfBABi5R6aSZlhgTWHxL+opYUDOnIGoNcJLwVYv0jLM= github.com/aws/aws-sdk-go-v2/service/kms v1.37.2/go.mod h1:dZYFcQwuoh+cLOlFnZItijZptmyDhRIkOKWFO1CfzV8= +github.com/aws/aws-sdk-go-v2/service/kms v1.37.3 h1:VpyBA6KP6JgzwokQps8ArQPGy9rFej8adwuuQGcduH8= +github.com/aws/aws-sdk-go-v2/service/kms v1.37.3/go.mod h1:TT/9V4PcmSPpd8LPUNJ8hBHJmpqcfhx6MrbWTkvyR+4= github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= +github.com/aws/aws-sdk-go-v2/service/route53 v1.46.0 h1:AaOWmXBSDSIEsTzx8Y2nYAxckgmBPNiRU5mjn/a9ynI= +github.com/aws/aws-sdk-go-v2/service/route53 v1.46.0/go.mod h1:IN9bx4yLAa3a3J7A41skQefcYObNv6ARAd2i5WxvGKg= github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= github.com/aws/aws-sdk-go-v2/service/sso v1.11.1 h1:DyHctRsJIAWIvom1Itb4T84D2jwpIu+KIi3d0SFaswg= github.com/aws/aws-sdk-go-v2/service/sso v1.11.1/go.mod h1:CvFTucADIx7U/M44vjLs/ZttpQHdpxwK+62+dUGhDeY= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 h1:UTpsIf0loCIWEbrqdLb+0RxnTXfWh2vhw4nQmFi4nPc= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.3/go.mod h1:FZ9j3PFHHAR+w0BSEjK955w5YD2UwB/l/H0yAK3MJvI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 h1:2YCmIXv3tmiItw0LlYf6v7gEHebLY45kBEnPezbUKyU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3/go.mod h1:u19stRyNPxGhj6dRm+Cdgu6N75qnbW7+QN0q0dsAk58= github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= github.com/aws/aws-sdk-go-v2/service/sts v1.16.1 h1:xsOtPAvHqhvQvBza5ohaUcfq1LceH2lZKMUGZJKiZiM= github.com/aws/aws-sdk-go-v2/service/sts v1.16.1/go.mod h1:Aq2/Qggh2oemSfyHH+EO4UBbgWG6zFCXLHYI4ILTY7w= +github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 h1:wVnQ6tigGsRqSWDEEyH6lSAJ9OyFUsSnbaUWChuSGzs= +github.com/aws/aws-sdk-go-v2/service/sts v1.32.3/go.mod h1:VZa9yTFyj4o10YGsmDO4gbQJUvvhY72fhumT8W4LqsE= github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.11.1/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= diff --git a/node/hack/accountant/send_obs.go b/node/hack/accountant/send_obs.go index 63bcb8d94c..f64bdbf843 100644 --- a/node/hack/accountant/send_obs.go +++ b/node/hack/accountant/send_obs.go @@ -45,7 +45,7 @@ func main() { ) logger.Info("Initializing guardian signer", zap.String("guardianSignerUri", guardianSignerUri)) - guardianSigner, err := guardiansigner.NewGuardianSignerFromUri(guardianSignerUri, true) + guardianSigner, err := guardiansigner.NewGuardianSignerFromUri(ctx, guardianSignerUri, true) if err != nil { logger.Fatal("failed to load guardian key", zap.Error(err)) diff --git a/node/pkg/accountant/accountant.go b/node/pkg/accountant/accountant.go index b2eb99f97b..9b096cc2e1 100644 --- a/node/pkg/accountant/accountant.go +++ b/node/pkg/accountant/accountant.go @@ -136,7 +136,7 @@ func NewAccountant( enforceFlag: enforceFlag, guardianSigner: guardianSigner, gst: gst, - guardianAddr: ethCrypto.PubkeyToAddress(guardianSigner.PublicKey()), + guardianAddr: ethCrypto.PubkeyToAddress(guardianSigner.PublicKey(ctx)), msgChan: msgChan, tokenBridges: make(validEmitters), pendingTransfers: make(map[string]*pendingEntry), diff --git a/node/pkg/accountant/submit_obs.go b/node/pkg/accountant/submit_obs.go index 2a4658f6f3..513e14a210 100644 --- a/node/pkg/accountant/submit_obs.go +++ b/node/pkg/accountant/submit_obs.go @@ -342,7 +342,7 @@ func SubmitObservationsToContract( return nil, fmt.Errorf("failed to sign accountant Observation request: %w", err) } - sigBytes, err := guardianSigner.Sign(digest.Bytes()) + sigBytes, err := guardianSigner.Sign(ctx, digest.Bytes()) if err != nil { return nil, fmt.Errorf("failed to sign accountant Observation request: %w", err) } diff --git a/node/pkg/adminrpc/adminserver.go b/node/pkg/adminrpc/adminserver.go index 85e2b68b70..2f345b9807 100644 --- a/node/pkg/adminrpc/adminserver.go +++ b/node/pkg/adminrpc/adminserver.go @@ -1163,7 +1163,7 @@ func (s *nodePrivilegedService) SignExistingVAA(ctx context.Context, req *nodev1 } // Add local signature - sig, err := s.guardianSigner.Sign(v.SigningDigest().Bytes()) + sig, err := s.guardianSigner.Sign(ctx, v.SigningDigest().Bytes()) if err != nil { panic(err) } diff --git a/node/pkg/adminrpc/adminserver_test.go b/node/pkg/adminrpc/adminserver_test.go index f5f6ed6e98..82791b832c 100644 --- a/node/pkg/adminrpc/adminserver_test.go +++ b/node/pkg/adminrpc/adminserver_test.go @@ -95,7 +95,7 @@ func generateGuardianSigners(num int) (signers []guardiansigner.GuardianSigner, panic(err) } signers = append(signers, signer) - addrs = append(addrs, ethcrypto.PubkeyToAddress(signer.PublicKey())) + addrs = append(addrs, ethcrypto.PubkeyToAddress(signer.PublicKey(context.Background()))) } return } @@ -122,7 +122,7 @@ func generateMockVAA(gsIndex uint32, signers []guardiansigner.GuardianSigner, t Payload: []byte("test"), } for i, signer := range signers { - sig, err := signer.Sign(v.SigningDigest().Bytes()) + sig, err := signer.Sign(context.Background(), v.SigningDigest().Bytes()) if err != nil { require.NoError(t, err) } @@ -164,7 +164,7 @@ func setupAdminServerForVAASigning(gsIndex uint32, gsAddrs []common.Address) *no governor: nil, evmConnector: connector, guardianSigner: guardianSigner, - guardianAddress: ethcrypto.PubkeyToAddress(guardianSigner.PublicKey()), + guardianAddress: ethcrypto.PubkeyToAddress(guardianSigner.PublicKey(context.Background())), } } diff --git a/node/pkg/governor/governor_monitoring.go b/node/pkg/governor/governor_monitoring.go index fe7118988a..678e658487 100644 --- a/node/pkg/governor/governor_monitoring.go +++ b/node/pkg/governor/governor_monitoring.go @@ -75,6 +75,7 @@ package governor import ( + "context" "fmt" "sort" "time" @@ -600,7 +601,7 @@ func (gov *ChainGovernor) publishConfig(hb *gossipv1.Heartbeat, sendC chan<- []b digest := ethCrypto.Keccak256Hash(append(governorMessagePrefixConfig, b...)) - sig, err := guardianSigner.Sign(digest.Bytes()) + sig, err := guardianSigner.Sign(context.Background(), digest.Bytes()) if err != nil { panic(err) } @@ -685,7 +686,7 @@ func (gov *ChainGovernor) publishStatus(hb *gossipv1.Heartbeat, sendC chan<- []b digest := ethCrypto.Keccak256Hash(append(governorMessagePrefixStatus, b...)) - sig, err := guardianSigner.Sign(digest.Bytes()) + sig, err := guardianSigner.Sign(context.Background(), digest.Bytes()) if err != nil { panic(err) } diff --git a/node/pkg/guardiansigner/amazonkms.go b/node/pkg/guardiansigner/amazonkms.go index c7167de28a..81927448f4 100644 --- a/node/pkg/guardiansigner/amazonkms.go +++ b/node/pkg/guardiansigner/amazonkms.go @@ -8,6 +8,8 @@ import ( "errors" "fmt" "math/big" + "strings" + "time" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/kms" @@ -19,6 +21,7 @@ import ( var ( secp256k1N = ethcrypto.S256().Params().N secp256k1HalfN = new(big.Int).Div(secp256k1N, big.NewInt(2)) + KMS_TIMEOUT = time.Second * 15 ) type asn1EcSig struct { @@ -36,32 +39,84 @@ type asn1EcPublicKeyInfo struct { Parameters asn1.ObjectIdentifier } +// Read the region from an ARN string. +func getRegionFromArn(arn string) string { + // Information in ARNs are colon-separated + arn_parts := strings.Split(arn, ":") + + // https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html#arns-syntax + // The format of an ARN is arn:partition:service:region:account-id:resource-info, so + // the region is at index 3 + if len(arn) < 4 { + return "" + } + + return arn_parts[3] +} + type AmazonKms struct { - KeyId string - Region string - svc *kms.Client + keyId string + region string + publicKey ecdsa.PublicKey + svc *kms.Client } -func NewAmazonKmsSigner(unsafeDevMode bool, keyPath string) (*AmazonKms, error) { +func NewAmazonKmsSigner(ctx context.Context, unsafeDevMode bool, keyPath string) (*AmazonKms, error) { + timeoutCtx, cancel := context.WithTimeout(ctx, KMS_TIMEOUT) + defer cancel() + amazonKmsSigner := AmazonKms{ - KeyId: keyPath, + keyId: keyPath, + region: getRegionFromArn(keyPath), } - cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("eu-north-1")) + // Create a configuration object to create a new KMS client from. The region passed to + // `config.WithDefaultRegion()` must match the region in the actual ARN, otherwise the SDK throws + // an error. This is why the region is first extracted from the keyPath. + cfg, err := config.LoadDefaultConfig(timeoutCtx, config.WithDefaultRegion(amazonKmsSigner.region)) if err != nil { - return nil, errors.New("failed to load default config") + return nil, errors.New("Failed to load default config") } amazonKmsSigner.svc = kms.NewFromConfig(cfg) + // Get the public key here, and store it as a property. The reason for this is that the + // `GetPublicKey` call could return an error that must be handled, and it's better to handle + // it during signer creation where an error can still be returned and bubbled up. The public + // key should also not be changing during runtime, so reading it once from KMS and storing + // it in memory seems sensible. + pubKeyOutput, err := amazonKmsSigner.svc.GetPublicKey(timeoutCtx, &kms.GetPublicKeyInput{ + KeyId: aws.String(amazonKmsSigner.keyId), + }) + + if err != nil { + return nil, fmt.Errorf("KMS signer creation failed: %w", err) + } + + var asn1Pubkey asn1EcPublicKey + _, err = asn1.Unmarshal(pubKeyOutput.PublicKey, &asn1Pubkey) + + if err != nil { + return nil, fmt.Errorf("Failed to unmarshal KMS public key: %w", err) + } + + ecdsaPubkey := ecdsa.PublicKey{ + X: new(big.Int).SetBytes(asn1Pubkey.PublicKey.Bytes[1 : 1+32]), + Y: new(big.Int).SetBytes(asn1Pubkey.PublicKey.Bytes[1+32:]), + } + + amazonKmsSigner.publicKey = ecdsaPubkey + return &amazonKmsSigner, nil } -func (a *AmazonKms) Sign(hash []byte) (signature []byte, err error) { +func (a *AmazonKms) Sign(ctx context.Context, hash []byte) (signature []byte, err error) { + timeoutCtx, cancel := context.WithTimeout(ctx, KMS_TIMEOUT) + defer cancel() - // request signing - res, err := a.svc.Sign(context.TODO(), &kms.SignInput{ - KeyId: aws.String(a.KeyId), + // Call the AWS KMS service to sign the input hash. + res, err := a.svc.Sign(timeoutCtx, &kms.SignInput{ + KeyId: aws.String(a.keyId), Message: hash, SigningAlgorithm: kms_types.SigningAlgorithmSpecEcdsaSha256, MessageType: kms_types.MessageTypeDigest, @@ -71,7 +126,7 @@ func (a *AmazonKms) Sign(hash []byte) (signature []byte, err error) { return nil, fmt.Errorf("Signing failed: %w", err) } - // decode r and s values + // Decode r and s values r, s := derSignatureToRS(res.Signature) // if s is greater than secp256k1HalfN, we need to substract secp256k1N from it @@ -86,46 +141,49 @@ func (a *AmazonKms) Sign(hash []byte) (signature []byte, err error) { // AWS KMS does not provide the recovery id. But that doesn't matter too much, since we can // attempt recovery id's 0 and 1, and in the process ensure that the signature is valid. - expectedPublicKey := a.PublicKey() + expectedPublicKey := a.PublicKey(ctx) signature = append(r, s...) // try recovery id 0 ecSigWithRecid := append(signature, []byte{0}...) - pubkey, err := ethcrypto.SigToPub(hash[:], ecSigWithRecid) + pubkey, _ := ethcrypto.SigToPub(hash[:], ecSigWithRecid) if bytes.Equal(ethcrypto.CompressPubkey(pubkey), ethcrypto.CompressPubkey(&expectedPublicKey)) { return ecSigWithRecid, nil } ecSigWithRecid = append(signature, []byte{1}...) - pubkey, err = ethcrypto.SigToPub(hash[:], ecSigWithRecid) + pubkey, _ = ethcrypto.SigToPub(hash[:], ecSigWithRecid) // try recovery id 1 if bytes.Equal(ethcrypto.CompressPubkey(pubkey), ethcrypto.CompressPubkey(&expectedPublicKey)) { return ecSigWithRecid, nil } - return nil, fmt.Errorf("Failed to generate signature") + // Reaching this return implies that it wasn't possible to generate a valid signature. This shouldn't + // happen, unless there is something seriously wrong with the KMS service. + return nil, fmt.Errorf("Failed to generate valid signature") } -func (a *AmazonKms) PublicKey() ecdsa.PublicKey { - pubKeyOutput, _ := a.svc.GetPublicKey(context.TODO(), &kms.GetPublicKeyInput{ - KeyId: aws.String(a.KeyId), - }) +func (a *AmazonKms) PublicKey(ctx context.Context) ecdsa.PublicKey { + return a.publicKey +} - var asn1Pubkey asn1EcPublicKey - _, _ = asn1.Unmarshal(pubKeyOutput.PublicKey, &asn1Pubkey) +func (a *AmazonKms) Verify(ctx context.Context, sig []byte, hash []byte) (bool, error) { + timeoutCtx, cancel := context.WithTimeout(ctx, time.Second*15) + defer cancel() - ecdsaPubkey := ecdsa.PublicKey{ - X: new(big.Int).SetBytes(asn1Pubkey.PublicKey.Bytes[1 : 1+32]), - Y: new(big.Int).SetBytes(asn1Pubkey.PublicKey.Bytes[1+32:]), + // Use ethcrypto to recover the public key + recoveredPubKey, err := ethcrypto.SigToPub(hash, sig) + + if err != nil { + return false, err } - return ecdsaPubkey -} + // Load the KMS signer's public key + kmsPublicKey := a.PublicKey(timeoutCtx) -func (a *AmazonKms) Verify(sig []byte, hash []byte) (bool, error) { - return true, nil + return recoveredPubKey.Equal(kmsPublicKey), nil } // https://bitcoin.stackexchange.com/questions/92680/what-are-the-der-signature-and-sec-format @@ -139,16 +197,15 @@ func (a *AmazonKms) Verify(sig []byte, hash []byte) (bool, error) { // 8. the s value as a big-endian integer func derSignatureToRS(signature []byte) (rBytes []byte, sBytes []byte) { var sigAsn1 asn1EcSig - _, err := asn1.Unmarshal(signature, &sigAsn1) - - if err != nil { - panic(err) - } + asn1.Unmarshal(signature, &sigAsn1) return sigAsn1.R.Bytes, sigAsn1.S.Bytes - // return rBytes, sBytes } +// adjustBufferSize takes an input buffer and +// a) trims it down to 32 bytes, if the input length is greater than 32, or +// b) returns the input as-is, if the input length is equal to 32, or +// c) left-pads it to 32 bytes, if the input length is less than 32. func adjustBufferSize(b []byte) []byte { length := len(b) diff --git a/node/pkg/guardiansigner/benchmarksigner.go b/node/pkg/guardiansigner/benchmarksigner.go index d7478524d4..3485684a13 100644 --- a/node/pkg/guardiansigner/benchmarksigner.go +++ b/node/pkg/guardiansigner/benchmarksigner.go @@ -1,6 +1,7 @@ package guardiansigner import ( + "context" "crypto/ecdsa" "fmt" "time" @@ -10,8 +11,8 @@ type BenchmarkSigner struct { innerSigner GuardianSigner } -func NewBenchmarkSigner(unsafeDevMode bool, signerKeyPath string) (*BenchmarkSigner, error) { - innerSigner, err := NewGuardianSignerFromUri(signerKeyPath, unsafeDevMode) +func NewBenchmarkSigner(ctx context.Context, unsafeDevMode bool, signerKeyPath string) (*BenchmarkSigner, error) { + innerSigner, err := NewGuardianSignerFromUri(ctx, signerKeyPath, unsafeDevMode) if err != nil { return nil, fmt.Errorf("failed to create benchmark signer: %w", err) @@ -22,11 +23,11 @@ func NewBenchmarkSigner(unsafeDevMode bool, signerKeyPath string) (*BenchmarkSig }, nil } -func (b *BenchmarkSigner) Sign(hash []byte) ([]byte, error) { +func (b *BenchmarkSigner) Sign(ctx context.Context, hash []byte) ([]byte, error) { start := time.Now() - sig, err := b.innerSigner.Sign(hash) + sig, err := b.innerSigner.Sign(ctx, hash) duration := time.Since(start) fmt.Printf("Signing execution time: %v\n", duration) @@ -34,11 +35,11 @@ func (b *BenchmarkSigner) Sign(hash []byte) ([]byte, error) { return sig, err } -func (b *BenchmarkSigner) PublicKey() ecdsa.PublicKey { +func (b *BenchmarkSigner) PublicKey(ctx context.Context) ecdsa.PublicKey { start := time.Now() - pubKey := b.innerSigner.PublicKey() + pubKey := b.innerSigner.PublicKey(ctx) duration := time.Since(start) fmt.Printf("Public key retrieval time: %v\n", duration) @@ -46,11 +47,11 @@ func (b *BenchmarkSigner) PublicKey() ecdsa.PublicKey { return pubKey } -func (b *BenchmarkSigner) Verify(sig []byte, hash []byte) (bool, error) { +func (b *BenchmarkSigner) Verify(ctx context.Context, sig []byte, hash []byte) (bool, error) { start := time.Now() - valid, err := b.innerSigner.Verify(sig, hash) + valid, err := b.innerSigner.Verify(ctx, sig, hash) duration := time.Since(start) fmt.Printf("Signature verification time: %v\n", duration) diff --git a/node/pkg/guardiansigner/filesigner.go b/node/pkg/guardiansigner/filesigner.go index 991eb4674a..d4dca63080 100644 --- a/node/pkg/guardiansigner/filesigner.go +++ b/node/pkg/guardiansigner/filesigner.go @@ -1,6 +1,7 @@ package guardiansigner import ( + "context" "crypto/ecdsa" "errors" "fmt" @@ -24,7 +25,7 @@ const ( GuardianKeyArmoredBlock = "WORMHOLE GUARDIAN PRIVATE KEY" ) -func NewFileSigner(unsafeDevMode bool, signerKeyPath string) (*FileSigner, error) { +func NewFileSigner(ctx context.Context, unsafeDevMode bool, signerKeyPath string) (*FileSigner, error) { fileSigner := &FileSigner{ keyPath: signerKeyPath, } @@ -67,7 +68,7 @@ func NewFileSigner(unsafeDevMode bool, signerKeyPath string) (*FileSigner, error return fileSigner, nil } -func (fs *FileSigner) Sign(hash []byte) ([]byte, error) { +func (fs *FileSigner) Sign(ctx context.Context, hash []byte) ([]byte, error) { // Sign the hash sig, err := crypto.Sign(hash, fs.privateKey) @@ -79,11 +80,11 @@ func (fs *FileSigner) Sign(hash []byte) ([]byte, error) { return sig, nil } -func (fs *FileSigner) PublicKey() ecdsa.PublicKey { +func (fs *FileSigner) PublicKey(ctx context.Context) ecdsa.PublicKey { return fs.privateKey.PublicKey } -func (fs *FileSigner) Verify(sig []byte, hash []byte) (bool, error) { +func (fs *FileSigner) Verify(ctx context.Context, sig []byte, hash []byte) (bool, error) { recoveredPubKey, err := ethcrypto.SigToPub(hash, sig) diff --git a/node/pkg/guardiansigner/generatedsigner.go b/node/pkg/guardiansigner/generatedsigner.go index 503fccd5ea..879e24051f 100644 --- a/node/pkg/guardiansigner/generatedsigner.go +++ b/node/pkg/guardiansigner/generatedsigner.go @@ -1,6 +1,7 @@ package guardiansigner import ( + "context" "crypto/ecdsa" "crypto/rand" "fmt" @@ -25,7 +26,7 @@ func NewGeneratedSigner(key *ecdsa.PrivateKey) (*GeneratedSigner, error) { } -func (gs *GeneratedSigner) Sign(hash []byte) (sig []byte, err error) { +func (gs *GeneratedSigner) Sign(ctx context.Context, hash []byte) (sig []byte, err error) { // Sign the hash sig, err = ethcrypto.Sign(hash, gs.privateKey) @@ -36,11 +37,11 @@ func (gs *GeneratedSigner) Sign(hash []byte) (sig []byte, err error) { return sig, nil } -func (gs *GeneratedSigner) PublicKey() (pubKey ecdsa.PublicKey) { +func (gs *GeneratedSigner) PublicKey(ctx context.Context) (pubKey ecdsa.PublicKey) { return gs.privateKey.PublicKey } -func (gs *GeneratedSigner) Verify(sig []byte, hash []byte) (valid bool, err error) { +func (gs *GeneratedSigner) Verify(ctx context.Context, sig []byte, hash []byte) (valid bool, err error) { recoveredPubKey, err := ethcrypto.SigToPub(hash, sig) if err != nil { diff --git a/node/pkg/guardiansigner/guardiansigner.go b/node/pkg/guardiansigner/guardiansigner.go index d0e9ccba94..d2828b2eba 100644 --- a/node/pkg/guardiansigner/guardiansigner.go +++ b/node/pkg/guardiansigner/guardiansigner.go @@ -1,6 +1,7 @@ package guardiansigner import ( + "context" "crypto/ecdsa" "errors" "fmt" @@ -23,16 +24,16 @@ const ( // GuardianSigner interface type GuardianSigner interface { // Sign expects a keccak256 hash that needs to be signed. - Sign(hash []byte) (sig []byte, err error) + Sign(ctx context.Context, hash []byte) (sig []byte, err error) // PublicKey returns the ECDSA public key of the signer. Note that this should not // be confused with the EVM address. - PublicKey() (pubKey ecdsa.PublicKey) + PublicKey(ctx context.Context) (pubKey ecdsa.PublicKey) // Verify is a convenience function that recovers a public key from the sig/hash pair, // and checks if the public key matches that of the guardian signer. - Verify(sig []byte, hash []byte) (valid bool, err error) + Verify(ctx context.Context, sig []byte, hash []byte) (valid bool, err error) } -func NewGuardianSignerFromUri(signerUri string, unsafeDevMode bool) (GuardianSigner, error) { +func NewGuardianSignerFromUri(ctx context.Context, signerUri string, unsafeDevMode bool) (GuardianSigner, error) { // Get the signer type signerType, signerKeyConfig, err := ParseSignerUri(signerUri) @@ -43,11 +44,11 @@ func NewGuardianSignerFromUri(signerUri string, unsafeDevMode bool) (GuardianSig switch signerType { case FileSignerType: - return NewFileSigner(unsafeDevMode, signerKeyConfig) + return NewFileSigner(ctx, unsafeDevMode, signerKeyConfig) case AmazonKmsSignerType: - return NewAmazonKmsSigner(unsafeDevMode, signerKeyConfig) + return NewAmazonKmsSigner(ctx, unsafeDevMode, signerKeyConfig) case BenchmarkSignerType: - return NewBenchmarkSigner(unsafeDevMode, signerKeyConfig) + return NewBenchmarkSigner(ctx, unsafeDevMode, signerKeyConfig) default: return nil, errors.New("unsupported guardian signer type") } diff --git a/node/pkg/guardiansigner/guardiansigner_test.go b/node/pkg/guardiansigner/guardiansigner_test.go index a927afdb66..2351b293ed 100644 --- a/node/pkg/guardiansigner/guardiansigner_test.go +++ b/node/pkg/guardiansigner/guardiansigner_test.go @@ -1,6 +1,8 @@ package guardiansigner import ( + "context" + "encoding/hex" "testing" "github.com/ethereum/go-ethereum/crypto" @@ -47,17 +49,18 @@ func TestFileSignerNonExistentFile(t *testing.T) { nonexistentFileUri := "file://somewhere/on/disk.key" // Attempt to generate signer using top-level generator - _, err := NewGuardianSignerFromUri(nonexistentFileUri, true) + _, err := NewGuardianSignerFromUri(context.Background(), nonexistentFileUri, true) assert.Error(t, err) // Attempt to generate signer using NewFileSigner _, keyPath, _ := ParseSignerUri(nonexistentFileUri) - fileSigner, err := NewFileSigner(true, keyPath) + fileSigner, err := NewFileSigner(context.Background(), true, keyPath) assert.Nil(t, fileSigner) assert.Error(t, err) } func TestFileSigner(t *testing.T) { + ctx := context.Background() fileUri := "file://../query/dev.guardian.key" expectedEthAddress := "0xbeFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe" @@ -68,33 +71,76 @@ func TestFileSigner(t *testing.T) { // matches the expected address. // Attempt to generate signer using top-level generator - fileSigner1, err := NewGuardianSignerFromUri(fileUri, true) + fileSigner1, err := NewGuardianSignerFromUri(ctx, fileUri, true) require.NoError(t, err) assert.NotNil(t, fileSigner1) - assert.Equal(t, ethcrypto.PubkeyToAddress(fileSigner1.PublicKey()).Hex(), expectedEthAddress) + assert.Equal(t, ethcrypto.PubkeyToAddress(fileSigner1.PublicKey(ctx)).Hex(), expectedEthAddress) // Attempt to generate signer using NewFileSigner signerType, keyPath, err := ParseSignerUri(fileUri) assert.Equal(t, signerType, FileSignerType) require.NoError(t, err) - fileSigner2, err := NewFileSigner(true, keyPath) + fileSigner2, err := NewFileSigner(ctx, true, keyPath) require.NoError(t, err) assert.NotNil(t, fileSigner2) - assert.Equal(t, ethcrypto.PubkeyToAddress(fileSigner2.PublicKey()).Hex(), expectedEthAddress) + assert.Equal(t, ethcrypto.PubkeyToAddress(fileSigner2.PublicKey(ctx)).Hex(), expectedEthAddress) // Sign some arbitrary data data := crypto.Keccak256Hash([]byte("data")) - sig, err := fileSigner1.Sign(data.Bytes()) + sig, err := fileSigner1.Sign(ctx, data.Bytes()) assert.NoError(t, err) // Verify the signature - valid, _ := fileSigner1.Verify(sig, data.Bytes()) + valid, _ := fileSigner1.Verify(ctx, sig, data.Bytes()) assert.True(t, valid) // Use generated signature with incorrect hash, should fail arbitraryHash := crypto.Keccak256Hash([]byte("arbitrary hash data")) - valid, _ = fileSigner1.Verify(sig, arbitraryHash.Bytes()) + valid, _ = fileSigner1.Verify(ctx, sig, arbitraryHash.Bytes()) assert.False(t, valid) } + +func TestAmazonKmsAdjustBufferSize(t *testing.T) { + + bytes_30_null_0102, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000102") + bytes_33_01, _ := hex.DecodeString("010101010101010101010101010101010101010101010101010101010101010101") + bytes_32_01, _ := hex.DecodeString("0101010101010101010101010101010101010101010101010101010101010101") + + full_of_null_bytes, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") + + tests := []struct { + name string + input []byte + expectedOutput []byte + }{ + { + name: "LeftPadSmallInput", + input: []byte{0x1, 0x2}, + expectedOutput: bytes_30_null_0102, + }, + { + name: "TruncateLargeInput", + input: bytes_33_01, + expectedOutput: bytes_32_01, + }, + { + name: "Leave32ByteInputAsIs", + input: bytes_32_01, + expectedOutput: bytes_32_01, + }, + { + name: "Return32NullBytesOnEmptyInput", + input: []byte{}, + expectedOutput: full_of_null_bytes, + }, + } + + for _, testcase := range tests { + t.Run(testcase.name, func(t *testing.T) { + output := adjustBufferSize(testcase.input) + assert.Equal(t, testcase.expectedOutput, output) + }) + } +} diff --git a/node/pkg/node/adminServiceRunnable.go b/node/pkg/node/adminServiceRunnable.go index 60717b5ab0..d083fbc148 100644 --- a/node/pkg/node/adminServiceRunnable.go +++ b/node/pkg/node/adminServiceRunnable.go @@ -90,7 +90,7 @@ func adminServiceRunnable( gov, evmConnector, guardianSigner, - ethcrypto.PubkeyToAddress(guardianSigner.PublicKey()), + ethcrypto.PubkeyToAddress(guardianSigner.PublicKey(ctx)), rpcMap, ) diff --git a/node/pkg/node/node_test.go b/node/pkg/node/node_test.go index 378f2acc0f..039b7396bb 100644 --- a/node/pkg/node/node_test.go +++ b/node/pkg/node/node_test.go @@ -122,7 +122,7 @@ func newMockGuardianSet(t testing.TB, testId uint, n int) []*mockGuardian { MockObservationC: make(chan *common.MessagePublication), MockSetC: make(chan *common.GuardianSet), guardianSigner: guardianSigner, - guardianAddr: eth_crypto.PubkeyToAddress(guardianSigner.PublicKey()), + guardianAddr: eth_crypto.PubkeyToAddress(guardianSigner.PublicKey(context.Background())), config: createGuardianConfig(t, testId, uint(i)), } } diff --git a/node/pkg/p2p/ccq_p2p.go b/node/pkg/p2p/ccq_p2p.go index 51e7056576..c0eb89adf4 100644 --- a/node/pkg/p2p/ccq_p2p.go +++ b/node/pkg/p2p/ccq_p2p.go @@ -246,7 +246,7 @@ func (ccq *ccqP2p) publisher(ctx context.Context, guardianSigner guardiansigner. continue } digest := query.GetQueryResponseDigestFromBytes(msgBytes) - sig, err := guardianSigner.Sign(digest.Bytes()) + sig, err := guardianSigner.Sign(ctx, digest.Bytes()) if err != nil { panic(err) } diff --git a/node/pkg/p2p/p2p.go b/node/pkg/p2p/p2p.go index fc6f2d8c98..ab7f9e2ffc 100644 --- a/node/pkg/p2p/p2p.go +++ b/node/pkg/p2p/p2p.go @@ -501,7 +501,7 @@ func Run(params *RunParams) func(ctx context.Context) error { // Start up heartbeating if it is enabled. if params.nodeName != "" { go func() { - ourAddr := ethcrypto.PubkeyToAddress(params.guardianSigner.PublicKey()) + ourAddr := ethcrypto.PubkeyToAddress(params.guardianSigner.PublicKey(ctx)) ctr := int64(0) // Guardians should send out their first heartbeat immediately to speed up test runs. @@ -679,7 +679,7 @@ func Run(params *RunParams) func(ctx context.Context) error { // Sign the observation request using our node's guardian key. digest := signedObservationRequestDigest(b) - sig, err := params.guardianSigner.Sign(digest.Bytes()) + sig, err := params.guardianSigner.Sign(ctx, digest.Bytes()) if err != nil { panic(err) } @@ -687,7 +687,7 @@ func Run(params *RunParams) func(ctx context.Context) error { sReq := &gossipv1.SignedObservationRequest{ ObservationRequest: b, Signature: sig, - GuardianAddr: ethcrypto.PubkeyToAddress(params.guardianSigner.PublicKey()).Bytes(), + GuardianAddr: ethcrypto.PubkeyToAddress(params.guardianSigner.PublicKey(ctx)).Bytes(), } envelope := &gossipv1.GossipMessage{ @@ -807,7 +807,7 @@ func Run(params *RunParams) func(ctx context.Context) error { zap.String("from", envelope.GetFrom().String())) } else { guardianAddr := eth_common.BytesToAddress(s.GuardianAddr) - if params.guardianSigner == nil || guardianAddr != ethcrypto.PubkeyToAddress(params.guardianSigner.PublicKey()) { + if params.guardianSigner == nil || guardianAddr != ethcrypto.PubkeyToAddress(params.guardianSigner.PublicKey(ctx)) { prevPeerId, ok := params.components.ProtectedHostByGuardianKey[guardianAddr] if ok { if prevPeerId != peerId { @@ -1034,7 +1034,7 @@ func Run(params *RunParams) func(ctx context.Context) error { zap.String("from", envelope.GetFrom().String())) } else { guardianAddr := eth_common.BytesToAddress(s.GuardianAddr) - if params.guardianSigner == nil || guardianAddr != ethcrypto.PubkeyToAddress(params.guardianSigner.PublicKey()) { + if params.guardianSigner == nil || guardianAddr != ethcrypto.PubkeyToAddress(params.guardianSigner.PublicKey(ctx)) { prevPeerId, ok := params.components.ProtectedHostByGuardianKey[guardianAddr] if ok { if prevPeerId != peerId { @@ -1162,7 +1162,8 @@ func Run(params *RunParams) func(ctx context.Context) error { } func createSignedHeartbeat(guardianSigner guardiansigner.GuardianSigner, heartbeat *gossipv1.Heartbeat) *gossipv1.SignedHeartbeat { - ourAddr := ethcrypto.PubkeyToAddress(guardianSigner.PublicKey()) + ctx := context.Background() + ourAddr := ethcrypto.PubkeyToAddress(guardianSigner.PublicKey(ctx)) b, err := proto.Marshal(heartbeat) if err != nil { @@ -1171,7 +1172,7 @@ func createSignedHeartbeat(guardianSigner guardiansigner.GuardianSigner, heartbe // Sign the heartbeat using our node's guardian signer. digest := heartbeatDigest(b) - sig, err := guardianSigner.Sign(digest.Bytes()) + sig, err := guardianSigner.Sign(ctx, digest.Bytes()) if err != nil { panic(err) } diff --git a/node/pkg/p2p/p2p_test.go b/node/pkg/p2p/p2p_test.go index c77aa1eaf6..767b7efd18 100644 --- a/node/pkg/p2p/p2p_test.go +++ b/node/pkg/p2p/p2p_test.go @@ -1,6 +1,7 @@ package p2p import ( + "context" "testing" "time" @@ -29,7 +30,7 @@ func TestSignedHeartbeat(t *testing.T) { guardianSigner, err := guardiansigner.GenerateSignerWithPrivatekeyUnsafe(nil) assert.NoError(t, err) - gAddr := crypto.PubkeyToAddress(guardianSigner.PublicKey()) + gAddr := crypto.PubkeyToAddress(guardianSigner.PublicKey(context.Background())) fromP2pId, err := peer.Decode("12D3KooWSgMXkhzTbKTeupHYmyG7sFJ5LpVreQcwVnX8RD7LBpy9") assert.NoError(t, err) p2pNodeId, err := fromP2pId.Marshal() @@ -37,7 +38,7 @@ func TestSignedHeartbeat(t *testing.T) { guardianSigner2, err := guardiansigner.GenerateSignerWithPrivatekeyUnsafe(nil) assert.NoError(t, err) - gAddr2 := crypto.PubkeyToAddress(guardianSigner2.PublicKey()) + gAddr2 := crypto.PubkeyToAddress(guardianSigner2.PublicKey(context.Background())) fromP2pId2, err := peer.Decode("12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU") assert.NoError(t, err) p2pNodeId2, err := fromP2pId2.Marshal() @@ -94,7 +95,7 @@ func TestSignedHeartbeat(t *testing.T) { testFunc := func(t *testing.T, tc testCase) { - addr := crypto.PubkeyToAddress(guardianSigner.PublicKey()) + addr := crypto.PubkeyToAddress(guardianSigner.PublicKey(context.Background())) heartbeat := &gossipv1.Heartbeat{ NodeName: "someNode", diff --git a/node/pkg/p2p/watermark_test.go b/node/pkg/p2p/watermark_test.go index ce4dfadaca..7c99ede7c9 100644 --- a/node/pkg/p2p/watermark_test.go +++ b/node/pkg/p2p/watermark_test.go @@ -119,7 +119,7 @@ func TestWatermark(t *testing.T) { gs[i].components.Port = uint(LOCAL_P2P_PORTRANGE_START + i) gs[i].networkID = "/wormhole/localdev" - guardianset.Keys = append(guardianset.Keys, crypto.PubkeyToAddress(gs[i].guardianSigner.PublicKey())) + guardianset.Keys = append(guardianset.Keys, crypto.PubkeyToAddress(gs[i].guardianSigner.PublicKey(ctx))) id, err := p2ppeer.IDFromPublicKey(gs[0].priv.GetPublic()) require.NoError(t, err) diff --git a/node/pkg/processor/benchmark_test.go b/node/pkg/processor/benchmark_test.go index 0995317940..21a0d0d6e7 100644 --- a/node/pkg/processor/benchmark_test.go +++ b/node/pkg/processor/benchmark_test.go @@ -143,9 +143,9 @@ func createProcessorForTest(b *testing.B, numVAAs int, ctx context.Context, db * for count := 0; count < 19; count++ { guardianSigner, err := guardiansigner.GenerateSignerWithPrivatekeyUnsafe(nil) require.NoError(b, err) - keys = append(keys, crypto.PubkeyToAddress(guardianSigner.PublicKey())) + keys = append(keys, crypto.PubkeyToAddress(guardianSigner.PublicKey(ctx))) guardianSigners = append(guardianSigners, guardianSigner) - guardianAddrs = append(guardianAddrs, crypto.PubkeyToAddress(guardianSigner.PublicKey()).Bytes()) + guardianAddrs = append(guardianAddrs, crypto.PubkeyToAddress(guardianSigner.PublicKey(ctx)).Bytes()) if count == 0 { ourSigner = guardianSigner } @@ -179,7 +179,7 @@ func createProcessorForTest(b *testing.B, numVAAs int, ctx context.Context, db * db: db, logger: logger, state: &aggregationState{observationMap{}}, - ourAddr: crypto.PubkeyToAddress(ourSigner.PublicKey()), + ourAddr: crypto.PubkeyToAddress(ourSigner.PublicKey(context.Background())), pythnetVaas: make(map[string]PythNetVaaEntry), updatedVAAs: make(map[string]*updateVaaEntry), gatewayRelayer: gwRelayer, @@ -231,7 +231,7 @@ func (pd *ProcessorData) createObservation(b *testing.B, guardianIdx int, k *com // Sign the digest using our node's guardian signer guardianSigner := pd.guardianSigners[guardianIdx] - signature, err := guardianSigner.Sign(digest.Bytes()) + signature, err := guardianSigner.Sign(context.Background(), digest.Bytes()) require.NoError(b, err) return &gossipv1.Observation{ diff --git a/node/pkg/processor/message.go b/node/pkg/processor/message.go index ed5946a330..933bd3452f 100644 --- a/node/pkg/processor/message.go +++ b/node/pkg/processor/message.go @@ -1,6 +1,7 @@ package processor import ( + "context" "encoding/hex" "time" @@ -69,7 +70,7 @@ func (p *Processor) handleMessage(k *common.MessagePublication) { hash := hex.EncodeToString(digest.Bytes()) // Sign the digest using the node's GuardianSigner - signature, err := p.guardianSigner.Sign(digest.Bytes()) + signature, err := p.guardianSigner.Sign(context.Background(), digest.Bytes()) if err != nil { panic(err) } diff --git a/node/pkg/processor/processor.go b/node/pkg/processor/processor.go index e483996505..e542a9ed6a 100644 --- a/node/pkg/processor/processor.go +++ b/node/pkg/processor/processor.go @@ -253,7 +253,7 @@ func NewProcessor( logger: supervisor.Logger(ctx), state: &aggregationState{observationMap{}}, - ourAddr: crypto.PubkeyToAddress(guardianSigner.PublicKey()), + ourAddr: crypto.PubkeyToAddress(guardianSigner.PublicKey(ctx)), governor: g, acct: acct, acctReadC: acctReadC, From 12b4275ab991a13266b0b5ed5c4052cb806af76c Mon Sep 17 00:00:00 2001 From: pleasew8t Date: Wed, 6 Nov 2024 20:43:29 +0200 Subject: [PATCH 4/4] add histograms to benchmark signer --- node/pkg/guardiansigner/benchmarksigner.go | 43 +++++++++++++++------- node/pkg/guardiansigner/guardiansigner.go | 2 +- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/node/pkg/guardiansigner/benchmarksigner.go b/node/pkg/guardiansigner/benchmarksigner.go index 3485684a13..187d7e2bdb 100644 --- a/node/pkg/guardiansigner/benchmarksigner.go +++ b/node/pkg/guardiansigner/benchmarksigner.go @@ -5,12 +5,36 @@ import ( "crypto/ecdsa" "fmt" "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" ) type BenchmarkSigner struct { innerSigner GuardianSigner } +var ( + guardianSignerSigningLatency = promauto.NewHistogram( + prometheus.HistogramOpts{ + Name: "wormhole_guardian_signer_signing_latency_us", + Help: "Latency histogram for Guardian signing requests", + Buckets: []float64{10.0, 20.0, 50.0, 100.0, 1000.0, 5000.0, 10000.0, 100_000.0, 1_000_000.0, 10_000_000.0, 100_000_000.0, 1_000_000_000.0}, + }) + + guardianSignerVerifyLatency = promauto.NewHistogram( + prometheus.HistogramOpts{ + Name: "wormhole_guardian_signer_sig_verify_latency_us", + Help: "Latency histogram for Guardian signature verification requests", + Buckets: []float64{10.0, 20.0, 50.0, 100.0, 1000.0, 5000.0, 10000.0, 100_000.0, 1_000_000.0, 10_000_000.0, 100_000_000.0, 1_000_000_000.0}, + }) +) + +// The Benchmark signer simply wraps any other available signer, but records the latency of signing and signature +// verification operations into histograms. To use the Benchmark signer, use the `benchmark://` URI scheme, followed +// by the signer URI that needs to be benchmarked. For example: +// +// benchmark://amazonkms:// func NewBenchmarkSigner(ctx context.Context, unsafeDevMode bool, signerKeyPath string) (*BenchmarkSigner, error) { innerSigner, err := NewGuardianSignerFromUri(ctx, signerKeyPath, unsafeDevMode) @@ -26,35 +50,28 @@ func NewBenchmarkSigner(ctx context.Context, unsafeDevMode bool, signerKeyPath s func (b *BenchmarkSigner) Sign(ctx context.Context, hash []byte) ([]byte, error) { start := time.Now() - sig, err := b.innerSigner.Sign(ctx, hash) - duration := time.Since(start) - fmt.Printf("Signing execution time: %v\n", duration) + + // Add Observation to histogram + guardianSignerSigningLatency.Observe(float64(duration.Microseconds())) return sig, err } func (b *BenchmarkSigner) PublicKey(ctx context.Context) ecdsa.PublicKey { - - start := time.Now() - pubKey := b.innerSigner.PublicKey(ctx) - - duration := time.Since(start) - fmt.Printf("Public key retrieval time: %v\n", duration) - return pubKey } func (b *BenchmarkSigner) Verify(ctx context.Context, sig []byte, hash []byte) (bool, error) { start := time.Now() - valid, err := b.innerSigner.Verify(ctx, sig, hash) - duration := time.Since(start) - fmt.Printf("Signature verification time: %v\n", duration) + + // Add observation to histogram + guardianSignerVerifyLatency.Observe(float64(duration.Microseconds())) return valid, err } diff --git a/node/pkg/guardiansigner/guardiansigner.go b/node/pkg/guardiansigner/guardiansigner.go index d2828b2eba..7629fe0463 100644 --- a/node/pkg/guardiansigner/guardiansigner.go +++ b/node/pkg/guardiansigner/guardiansigner.go @@ -17,7 +17,7 @@ const ( FileSignerType // amazonkms:// AmazonKmsSignerType - // benchmark://:// + // benchmark:// BenchmarkSignerType )