Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GoSec action and fix issues #505

Merged
merged 16 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/gosec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Gosec
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "*" ]
jobs:
tests:
runs-on: ubuntu-latest
env:
GO111MODULE: on
steps:
- name: Checkout Source
uses: actions/checkout@v4
- name: Run Gosec Security Scanner
uses: securego/gosec@master
with:
args: ./...
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,8 @@ generate-jsons:
generate-ssz:
@go generate ./qbft/
@go generate ./ssv/
@go generate ./types/
@go generate ./types/

.PHONY: gosec
gosec:
gosec ./...
11 changes: 8 additions & 3 deletions qbft/round_robin_proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ import "github.com/ssvlabs/ssv-spec/types"
// Each new height has a different first round proposer which is +1 from the previous height.
// First height starts with index 0
func RoundRobinProposer(state *State, round Round) types.OperatorID {
firstRoundIndex := 0
committeeLength := uint64(len(state.CommitteeMember.Committee))

firstRoundIndex := uint64(0)

// Increment index using height
if state.Height != FirstHeight {
firstRoundIndex += int(state.Height) % len(state.CommitteeMember.Committee)
firstRoundIndex += uint64(state.Height) % committeeLength
}

index := (firstRoundIndex + int(round) - int(FirstRound)) % len(state.CommitteeMember.Committee)
// Increment index using round heights
index := (firstRoundIndex + uint64(round) - uint64(FirstRound)) % committeeLength
return state.CommitteeMember.Committee[index].OperatorID
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ func lateCommitPastInstanceStateComparison(height qbft.Height, lateMsg *types.Si
testingutils.TestingOperatorSigner(ks),
)

for i := 0; i <= int(height); i++ {
msgIndex := 0
for i := uint64(0); i <= uint64(height); i++ {
contr.Height = qbft.Height(i)

instance := &qbft.Instance{
Expand Down Expand Up @@ -126,7 +127,8 @@ func lateCommitPastInstanceStateComparison(height qbft.Height, lateMsg *types.Si
instance.ForceStop()
}

msgs := allMsgs[offset*i : offset*(i+1)]
msgs := allMsgs[offset*msgIndex : offset*(msgIndex+1)]
msgIndex += 1
comparable.SetSignedMessages(instance, msgs)

contr.StoredInstances = append([]*qbft.Instance{instance}, contr.StoredInstances...)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ func latePreparePastInstanceStateComparison(height qbft.Height, lateMsg *types.S
testingutils.TestingOperatorSigner(ks),
)

for i := 0; i <= int(height); i++ {
msgIndex := 0
for i := uint64(0); i <= uint64(height); i++ {
contr.Height = qbft.Height(i)

instance := &qbft.Instance{
Expand Down Expand Up @@ -126,7 +127,8 @@ func latePreparePastInstanceStateComparison(height qbft.Height, lateMsg *types.S
instance.ForceStop()
}

msgs := allMsgs[offset*i : offset*(i+1)]
msgs := allMsgs[offset*msgIndex : offset*(msgIndex+1)]
msgIndex += 1
comparable.SetSignedMessages(instance, msgs)

contr.StoredInstances = append([]*qbft.Instance{instance}, contr.StoredInstances...)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,11 @@ func lateProposalPastInstanceStateComparison(height qbft.Height, lateMsg *types.
testingutils.TestingOperatorSigner(ks),
)

for i := 0; i <= int(height); i++ {
msgIndex := 0
for i := uint64(0); i <= uint64(height); i++ {
contr.Height = qbft.Height(i)
msgs := allMsgs[offset*i : offset*(i+1)]
msgs := allMsgs[offset*msgIndex : offset*(msgIndex+1)]
msgIndex += 1

instance := &qbft.Instance{
StartValue: []byte{1, 2, 3, 4},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ func lateRoundChangePastInstanceStateComparison(height qbft.Height, lateMsg *typ
testingutils.TestingOperatorSigner(ks),
)

for i := 0; i <= int(height); i++ {
msgIndex := 0
for i := uint64(0); i <= uint64(height); i++ {
contr.Height = qbft.Height(i)

instance := &qbft.Instance{
Expand Down Expand Up @@ -129,7 +130,8 @@ func lateRoundChangePastInstanceStateComparison(height qbft.Height, lateMsg *typ
if qbft.Height(i) != height {
instance.ForceStop()
}
msgs := allMsgs[offset*i : offset*(i+1)]
msgs := allMsgs[offset*(msgIndex) : offset*(msgIndex+1)]
msgIndex += 1
comparable.SetSignedMessages(instance, msgs)

contr.StoredInstances = append([]*qbft.Instance{instance}, contr.StoredInstances...)
Expand Down
11 changes: 8 additions & 3 deletions qbft/spectest/tests/controller_spectest.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,16 @@ func (test *ControllerSpecTest) Run(t *testing.T) {
}

var lastErr error
for i, runData := range test.RunInstanceData {
height := qbft.Height(i)
currHeight := qbft.Height(0)
for _, runData := range test.RunInstanceData {
height := currHeight
if runData.Height != nil {
height = *runData.Height
}
if err := test.runInstanceWithData(t, height, contr, runData); err != nil {
lastErr = err
}
currHeight += 1
}

if len(test.ExpectedError) != 0 {
Expand Down Expand Up @@ -232,8 +234,9 @@ func (test *ControllerSpecTest) GetPostState() (interface{}, error) {
}

ret := make([]*qbft.Controller, len(test.RunInstanceData))
currHeight := qbft.Height(0)
for i, runData := range test.RunInstanceData {
height := qbft.Height(i)
height := currHeight
if runData.Height != nil {
height = *runData.Height
}
Expand All @@ -259,6 +262,8 @@ func (test *ControllerSpecTest) GetPostState() (interface{}, error) {
return nil, err
}
ret[i] = copied

currHeight += 1
}
return ret, nil
}
2 changes: 1 addition & 1 deletion qbft/spectest/tests/startinstance/prev_decided.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func previousDecidedStateComparison(height qbft.Height, decidedState bool) *comp
testingutils.TestingOperatorSigner(ks),
)

for i := 0; i <= int(height); i++ {
for i := uint64(0); i <= uint64(height); i++ {
contr.Height = qbft.Height(i)

instance := &qbft.Instance{
Expand Down
Binary file modified ssv/spectest/generate/tests.json.gz
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func MissingSomeShares() tests.SpecTest {
msgID := testingutils.CommitteeMsgID(ks)

// Committee's validator indexes
committeeShareValidators := []int{1, 3, 5, 7, 9}
committeeShareValidators := []phase0.ValidatorIndex{1, 3, 5, 7, 9}
// KeySet and Share map for Committee
committeeShareKSMap := testingutils.KeySetMapForValidatorIndexList(committeeShareValidators)
committeeShareMap := testingutils.ShareMapFromKeySetMap(committeeShareKSMap)
Expand Down
2 changes: 1 addition & 1 deletion ssv/spectest/tests/runner/postconsensus/valid_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func decideRunnerForData(r ssv.Runner, duty types.Duty, decidedValue []byte) ssv
break
}

quorum := (uint64(len(share.Committee)-1)/3)*2 + 1
quorum := ((uint64(len(share.Committee))-1)/3)*2 + 1
r.GetBaseRunner().State = ssv.NewRunnerState(quorum, duty)
r.GetBaseRunner().State.RunningInstance = qbft.NewInstance(
r.GetBaseRunner().QBFTController.GetConfig(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func MajoritySlashable() tests.SpecTest {

// Make slashable map with majority
slashableMap := make(map[string][]phase0.Slot)
for i := 0; i < int(keySet.Threshold); i++ {
for i := uint64(0); i < keySet.Threshold; i++ {
slashableMap[sharesPKString[i]] = []phase0.Slot{testingutils.TestingDutySlot}
}

Expand Down
38 changes: 33 additions & 5 deletions types/beacon_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,16 +231,44 @@ func (n BeaconNetwork) EstimatedCurrentSlot() spec.Slot {

// EstimatedSlotAtTime estimates slot at the given time
func (n BeaconNetwork) EstimatedSlotAtTime(time int64) spec.Slot {
genesis := int64(n.MinGenesisTime())
if time < genesis {
// Sanitize time
if time < 0 {
return 0
}
return spec.Slot(uint64(time-genesis) / uint64(n.SlotDurationSec().Seconds()))

// Get delta time
genesis := n.MinGenesisTime()
deltaTime := uint64(time) - genesis

// Get slot duration
slotDurationInSeconds := int64(n.SlotDurationSec().Seconds())
// Sanitize slot duration
if slotDurationInSeconds <= 0 {
return spec.Slot(math.MaxUint64)
}
Comment on lines +245 to +248
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering whether we need all of this in spec, especially since Slot Duration should be a constant...
GoSec yells if it isn't there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. But, yes, GoSec yells (and with reason tbh 😅)


return spec.Slot(uint64(deltaTime) / uint64(slotDurationInSeconds))
}

func (n BeaconNetwork) EstimatedTimeAtSlot(slot spec.Slot) int64 {
d := int64(slot) * int64(n.SlotDurationSec().Seconds())
return int64(n.MinGenesisTime()) + d
// Get slot duration
slotDurationInSeconds := int64(n.SlotDurationSec().Seconds())
if slotDurationInSeconds < 0 {
return int64(math.MaxInt64)
}

// Get delta to add to genesis time
d := uint64(slot) * uint64(slotDurationInSeconds)

// Get genesis time
minGenesisTime := n.MinGenesisTime()

// Sanitize variables
if minGenesisTime > uint64(math.MaxInt64) || d > uint64(math.MaxInt64) {
return int64(math.MaxInt64)
}

return int64(minGenesisTime) + int64(d)
Comment on lines +254 to +271
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

converting from uint64 to int64 is considered safe by gosec?
A big number can turn negative I think (no that it will happen but you know)

Copy link
Contributor Author

@MatheusFranco99 MatheusFranco99 Sep 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice that, before the conversion, sanity checks ensure it'll be converted properly. GoSec sees this. E.g., if I remove the checks, it will raise the error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think... that if the sum of int64(minGenesisTime) + int64(d) will be larger than math.MaxInt64 there will be an overflow... weird that go-sec didn't shout about that...

Anyhow, using some safe arithmetics here is an overkill since we both know that the above scenario will never happen and we don't want to have too many impl details in spec...
So if they shout I will try to talk to them about this

}

// EstimatedCurrentEpoch estimates the current epoch
Expand Down
2 changes: 1 addition & 1 deletion types/beacon_types_encoding.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 16 additions & 2 deletions types/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"encoding/hex"
"math"

"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
Expand Down Expand Up @@ -49,12 +50,25 @@ func (msg MessageID) GetDutyExecutorID() []byte {

func (msg MessageID) GetRoleType() RunnerRole {
roleByts := msg[roleTypeStartPos : roleTypeStartPos+roleTypeSize]
return RunnerRole(binary.LittleEndian.Uint32(roleByts))
roleValue := binary.LittleEndian.Uint32(roleByts)

// Sanitize RoleValue
if roleValue > math.MaxInt32 {
return RoleUnknown
}

return RunnerRole(roleValue)
}

func NewMsgID(domain DomainType, dutyExecutorID []byte, role RunnerRole) MessageID {

// Sanitize role. If bad role, return an empty MessageID
roleValue := int32(role)
if roleValue < 0 {
return MessageID{}
}
roleByts := make([]byte, 4)
binary.LittleEndian.PutUint32(roleByts, uint32(role))
binary.LittleEndian.PutUint32(roleByts, uint32(roleValue))

return newMessageID(domain[:], roleByts, dutyExecutorID)
}
Expand Down
2 changes: 1 addition & 1 deletion types/messages_encoding.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions types/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ type Operator struct {
// https://github.com/ConsenSys/qbft-formal-spec-and-verification/blob/main/dafny/spec/L1/node_auxiliary_functions.dfy#L259

func (cm *CommitteeMember) HasQuorum(cnt int) bool {
if cnt <= 0 {
return false
}
return uint64(cnt) >= cm.GetQuorum()
}

Expand All @@ -36,6 +39,9 @@ func (cm *CommitteeMember) GetQuorum() uint64 {
// https://github.com/ConsenSys/qbft-formal-spec-and-verification/blob/main/dafny/spec/L1/node_auxiliary_functions.dfy#L244

func (cm *CommitteeMember) HasPartialQuorum(cnt int) bool {
if cnt <= 0 {
return false
}
return uint64(cnt) >= cm.FaultyNodes+1
}

Expand Down
2 changes: 1 addition & 1 deletion types/operator_encoding.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"Name": "bls secret key encryption",
"SKPem": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBckZWNTk2TkdRRWV2VVN1UmtwWHNnMXN1ZzRJZzJITjh5aDlFVGx5NkFCY0t1cXlYCm0zd0VTbVg3T0VmZDdMRWk4MVRRdHYrTmFQUGZFV2M0UTJiRWNRSVZOL2hWUXVrL0JFZWQvTTBUbTVzQmZXZUgKMkl5OGVGU2VJVmxaZ3V1ZFFQdDdEWEdGS0xjQitKYmd3Szd3WENyeFQyRW56d2YwQ0ZMYWpUVFpJUE51MFhjYQpSMjFYNy9aQ0RvRUM2Yy9yVUcraFhLdG1ZMFNCenlDNkk3Qy9ocGI5RWExUW9GTjh4QXRvc3l2djVCVlRpZ0JCCi9BeWREUzRmMGtWc2dlLytUaDQrK3JNcnJzWXhXcnpENmQyZVM1UytPOU5mWWpLU2xLL1I0VGxuK1NvNDFVVE0KSGpuWjBscWN3RlVNeU8rdzVabS9FNWdReGNZR0VPMXBtRWQvWFFJREFRQUJBb0lCQUJRWHJ5M0JPcHFhQVFYTQp0NTlJblplL0ZOQStac2YxeHhIbHpWZjVsYklXL2FoQUlUaituNTF4QUhaU1lyeUs5cEU2VFU5WXdrci9TLzNDCmRCdmZxbjJtaVlUS0RsN0x4UTRocjNqZkRDOHpSbHd4cXZRRGpLSFc4OHpkbHdNZHAyc0JKeHF0SnFKVm5BUTQKeXlHTUEvZ3JCWkdFdVZoNUMrbkFoenk1Y2F0V3BaVGNNb2RENVZkMFFjVy8zNVpBRjEwdlk5Z0NEaDZsbjVXbApqbkxXdjBSSUEwTmlWMVFVMklvaXVYbnBHVmM5Wkk3bm5lTXJ3MUR4OU9XcDMxektVODBxdlVvK085UTIwbUpCCnhnVTdmWFVkdXhDbm1LRFJvTkxIdDhXTElNd0UrOFYvdDFkZ1F2aUF3aitjWWNpUElsemJmVTR5K3AyTDJzTUcKV1dWODIxMENnWUVBeithY0tjWDJ3Y2tJVFZ1M2VUWis3YmdYTU16aTV5aUVWYkQ0c3ArdDluWVBtOHNUa0xOdwp3cDU2Z2NQNjg3K0NrMTdVRHc0WDZMNmxMSVdPSkJvbkZkTlowcnpyd3U0RFBzaGNwN2FLN3ViTHpLU2pCZXd2CmVNQnlTenhYYVN6L1psRllFaE5xai90dVl5U1BpR1JqUjVwWTFKbzRFSDBzVUhQYy9adnZCMzhDZ1lFQTFEUlYKODRGcVh2WExHc3FuVWRBQmtSb0RHdnIvWjZjdEwxTTRuRUlBb3p6NmcxNTM0eWdsdnl4Q0E3RktpaWNZVFdBego2V1dDQ2dTM3VmenJIS0N2SDhiNE5ERExxV1pRV0h5WHlKdnY3V05uclZURFNVUFF1VUlscHlyTXJQb2tNaFdtCkFacEFXZHl4eFJJdmFJMm85WGtHTmw2eVc5MU9lOXVMWFkwYkJ5TUNnWUVBc0k4UFptYUo5ZTd0and6cUZ4WFIKMjc2d2F6QkZMcno1RGZFYWRGQXBwQnFGalBCODhER0QvTFFzSkJJMUNGWkc1VEx1Y3M2c1BXdlN5S212bWpkZgpwQ2gycXdMb1VnWmlXU285amV6M3RvWG81Q1daa1VrUTA4TFVEZEwwQlExQzVUa0Z5MndUM3ExUUJRQ2lxTmxnCmV3bTRrTzFiMlowRVNscnJmYkcwNEs4Q2dZRUFnNWx0VUZVSzVaY2kvUnQraG5NcjlaT2ZKMlZQYlRXMUJPdGsKYVN0WmYrSjZMV3d4aDFOSGpYWmgvaTQ5M3MxOStjWldpMERqVTFrM280VWhQYUM4MmtVbmVoNWt2MHB2TzJFUgpORnpZZjJ5dFNFWVAwZWpYa1h4ZkkzdWNjUTJ6MHNld0tzQkJkamt5bWRlOFJPZk5SMlpsbnVROVVsRTlzZndtClFyOFdhdWNDZ1lBdHhCWUF6OGE4YUxuclFZaWN6djd1L25mc1VmNTFmYjZWdGhPMXliN0c5Z0gxMmZvSXJhdjIKV0VKTWlGeWU0K052V1BFTnM1alUxVnVEWmVDbTlnYnRBZnZWZG83TGFGMlVVMlNZamdLOUFPT1EwYUlqOUNnMApDZjU3TUlVY0t2M2VUSjhzTmI2dGtJdXhUdkFDRENOeStzUU9lajNIMU05TnlaVE4ybllBeGc9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=",
"PKPem": "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBckZWNTk2TkdRRWV2VVN1UmtwWHMKZzFzdWc0SWcySE44eWg5RVRseTZBQmNLdXF5WG0zd0VTbVg3T0VmZDdMRWk4MVRRdHYrTmFQUGZFV2M0UTJiRQpjUUlWTi9oVlF1ay9CRWVkL00wVG01c0JmV2VIMkl5OGVGU2VJVmxaZ3V1ZFFQdDdEWEdGS0xjQitKYmd3Szd3ClhDcnhUMkVuendmMENGTGFqVFRaSVBOdTBYY2FSMjFYNy9aQ0RvRUM2Yy9yVUcraFhLdG1ZMFNCenlDNkk3Qy8KaHBiOUVhMVFvRk44eEF0b3N5dnY1QlZUaWdCQi9BeWREUzRmMGtWc2dlLytUaDQrK3JNcnJzWXhXcnpENmQyZQpTNVMrTzlOZllqS1NsSy9SNFRsbitTbzQxVVRNSGpuWjBscWN3RlVNeU8rdzVabS9FNWdReGNZR0VPMXBtRWQvClhRSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K",
"PlainText": "UxSHaLsZMK4rMnYgiUVacCNlSiieLZc3rW9cmWLlS5I=",
"CipherText": "Aa5+egQbmVaPlcRRSHOq10CIMwOOmcd9ermTtI88m6vEfDyuQkeIxMhc5FNhhBl+npTtVmmVf68eu96rEBT3BWWSwRZIvy7q6KnqA1LiiKqhBUXP1Wj7tDA2+wiV6+XJwDuCZCpOi1z+tjNlUz3E+xr+Wi7SW0hk6b9JLDElnmdQOF0cjuDpQORiWmq/nrmhA66yiMFpnngh82IRj0rrN4kCbYfwRATKXZtzcFpv6A4a3Q2nk/Zj9v1Adys37MD4+bIgkz8X7vVB02HJjnMOTJGrIkYoSblPHRIIM5X3D8QQ96ZBJ9SSSYRpkgr6mLxK83BcDGbJHS/9u44Mt4n1sQ=="
"SKPem": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBbTdObTM2Q2lkWlNwOHIyMkF3blRHR1RKZzVKNEZQOEhoY2J5cFllcEp5eFJCcklKClQ3RVNwRzk2ZGVKRW9XNmphTDhhUGFoTjBENTY2N3dyS0luZlpESDlwV2JIbkZXdzkydmFtTDd5akdQeHNPZHUKTVg2WS9kNDRiUVhJWEVyaGlYTjRFYUY5WTF0STNZRW1URVFxQjNJQUFuREJmUE16RU8zcis5aVRCNXFKT3pQYwo2elNHMXFvKzE2RUNqd0RJTm5aQkFmZXF4NUM4VGxFa1UrS3dkUWZwaEF4VTNUK1JPbDE3OW95aXh6alhNSllkCkt2ckNRbmNPaHY1cy9yeU1peFB6TUFtaFNIcFpnT1dnK05oTlo5ODZBMHNMMThuVTk0OEF4ZE9abDhFL1B1WTAKUXA5YmVUTFVLTDdKVVFlcWc3aUdvSEYxL0xMdFBwSW4xMnNOUXdJREFRQUJBb0lCQVFDWEd5WWtzKzNXOVA5MgpnRjBMVjlhUm53YmU5U0FyS0ZLeHB5SElPZitVNy9KMjhBUVBYa2M4WktCUmxkV0xZaWVldVpDSzlETUlmZ3FuCmk0ZkxJUHMzYnppOTlDQ1cvYWJ3aUxDdWV1cEVDNTc5VTYxaXhYMVBMQUpROUVLK0owSDVOcUg1WW1PaE1HOW4KNFRZODRBNTJDVkl2RENVTDBhN21xRERVeXh2dVRLMTZwZ2t5ZEp0aUh2cVBYZ2UxblNJUUtNSSt1OFlJNEpkSQpLSENwMWl3cVpBWGVQdVpoTjVTbmt1NHhzWkdoVmtNZjQ0TE9XdVZpeGFESi9FNjBlUWxreW81TDgyRnQyUmNrClJWdHB3WUZjYkFSSUhodVpTUDdqVGN0OGdMdEdsUGxWU1JUOFpmclQ0T21EU2F3Lytsb1BXOE1tVDdIdjUwWVAKWFFMclgrTlJBb0dCQU10Ykp1QUgrM1Q0RS93YXluVGx2Q3Y0aWNxM1EwUFA4bWh4SExicW5VV0Zub2JSQ24xLwpXOXEwaFZPTytmTXBzTTJmWm1uV3daQjZKK21DMHZ5cEJobTlsc1kwQTBEbGd2VGVJMzV4ZFQ4bGRjOEtMT3ZICm8xazBFR0JhTjgyV0VKVFdnNy9OMkJvYmF3TnFTSkhRcVR4VFFuaTVYNGhYU011dUZJUiswTUM3QW9HQkFNUUMKQ0EzSlZNTWhVTUF6MlppTzU3VVRJNVVXd1I0Mm5tZEhDdHk5bm5sZzZDek13WDdobzVCRjM0cGh3UHZnZFlBagpWcUNQcFN4ZHZuc2h6QTFtNlpRQlhmSVZ5QzlwOUtYclZxeHRoTkFJb2NHcm5ZaGZZQm1rVEdOSnRxVHljbEN5CnY0c1RGWi9qcE5sVUZNWXlMR1JTUFVpOE9wbC9iaHVoQ1lFcUhvRVpBb0dBZHp4a0ovb21NK2g5OTFWU0hvYlIKOWNwT0tRR1p2RHBDdllDTlFLZUNQZEJpS2xTSjNSbi9KdGF3VWxWRU01TGZhMEdxa0NadTZxTGxvaUttU2FWbAp3VlFNQXYxZVp2L3I4RjRMMjhqMDRXaTZrZ0k2WXFsMUd3blBERi81MWw3R0xDODNveEEwUk9LTXRienMvaXFtCkFJd2xMcG9xN011WkVHeHE5V0ZTVDU4Q2dZQjlyRi9GbHlVRng4S2l1WnYydVFuUGkrbndtWnBRNk93L2c4bHYKSnhVSTloMW5QQkdFYk9BV2pQWjdINXBBNVBYeHByYlFVOG12M0p0WkQ1NXBxV1p5UXo0ZERlSkFwRXI4Wnh3MwppakR4d2RjVStoZ1RiRE9OdlU1TkN1SlVlQzdibHdCQkI0ZUI4TSt0bUwrSkpIcGFDSERLeGdVOGpmdm5NeVdJCjc1eUhZUUtCZ1FDd3lVdkUrYWFHUGMyRjNVcFZJbUZCclovTUVjdVpqVllHQXpxa216YW4vUmc4dGJVa21jN1MKZ3RjUy9FV0s3SGY1U1dKd21NT1BvdDViOGlraVAybWV4US83ZzlmVS9TNGlib2VLYXh6aUxQVmZXbzlaeXdBbAoySTltcGc2RU1hUGR1UW9LS3RrOVR4ZkZhcXcvNU5xaUw0UmdWalhkd0RMQjE5OHNQUndTbVE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=",
"PKPem": "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBbTdObTM2Q2lkWlNwOHIyMkF3blQKR0dUSmc1SjRGUDhIaGNieXBZZXBKeXhSQnJJSlQ3RVNwRzk2ZGVKRW9XNmphTDhhUGFoTjBENTY2N3dyS0luZgpaREg5cFdiSG5GV3c5MnZhbUw3eWpHUHhzT2R1TVg2WS9kNDRiUVhJWEVyaGlYTjRFYUY5WTF0STNZRW1URVFxCkIzSUFBbkRCZlBNekVPM3IrOWlUQjVxSk96UGM2elNHMXFvKzE2RUNqd0RJTm5aQkFmZXF4NUM4VGxFa1UrS3cKZFFmcGhBeFUzVCtST2wxNzlveWl4empYTUpZZEt2ckNRbmNPaHY1cy9yeU1peFB6TUFtaFNIcFpnT1dnK05oTgpaOTg2QTBzTDE4blU5NDhBeGRPWmw4RS9QdVkwUXA5YmVUTFVLTDdKVVFlcWc3aUdvSEYxL0xMdFBwSW4xMnNOClF3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K",
"PlainText": "JEbl5FCK/rPt+AB/v2WQGjcdNVR41K4OgS66atTX05Y=",
"CipherText": "fF4SOXNm++SXN5G3pS+DKOV9KsZaFFIivhhw3ocKO5sBpBT2ha07YkN7hLV07Q+xMHyuazHhKtUjR7hHlqO4aLiYmpPKPlt0kDKmP+xm3kkJmEni//4KddxTUgoXdnZVas50Qs/0BkJ5ibSupqsDGgMiiPn6Chwjrv4R1XPGOv0RHyF6HGFmmrSPyixt3IY/TvVYkxRvqd+J7yI1BnsKGRv45+CW1pz4IZa+7aGvkY+WDj3BQRdPPaADGo2JaoE1t3jfadXC2VXDq3iCo8wAh4eQDPEFAP5L/BToEN9xma0OIsGLdWDkCnonqT837aNNU+4mdjChgndhBpz8E4fC6Q=="
}
Loading