Skip to content

Commit

Permalink
Spec - Add GoSec action and fix issues (#505)
Browse files Browse the repository at this point in the history
* Add github action and makefile command

* Fix issues in round robin proposer function

* Fix bad PutUint32 in GetCommitteeID

* Fix issue with HasQuorum and HasPartialQuorum

* Add role sanitization in GetRoleType and NewMessageType

* Add sanitization to BeaconNetwork methods

* Add sanitization in testingutils

* Add sanitization to height usage in test files

* Fix uint64 conversion in runner/postconsensus/valid_msg test

* Sanitize ValidatorIndex conversion

* Update action name

* Fix tests to use valid RunnerRoles

* Generate SSZ

* Generate JSON tests

* Revert the change on GetCommitteeID

* Add nosec G115 to GetCommitteeID
  • Loading branch information
MatheusFranco99 authored Sep 26, 2024
1 parent 97ed496 commit 93ad50e
Show file tree
Hide file tree
Showing 32 changed files with 196 additions and 74 deletions.
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)
}

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)
}

// 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.

2 changes: 1 addition & 1 deletion types/committee_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func GetCommitteeID(committee []OperatorID) CommitteeID {
// Convert to bytes
bytes := make([]byte, len(committee)*4)
for i, v := range committee {
binary.LittleEndian.PutUint32(bytes[i*4:], uint32(v))
binary.LittleEndian.PutUint32(bytes[i*4:], uint32(v)) // #nosec G115 --This conversion is kept since it would cause a network fork, and, for now, it is safe since OperatorID << MaxUInt32 as it's incremented by contract
}
// Hash
return sha256.Sum256(bytes)
Expand Down
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

0 comments on commit 93ad50e

Please sign in to comment.