diff --git a/.golangci.yml b/.golangci.yml index ede7cf0..6ad95c5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,120 +1,7 @@ -linters-settings: - funlen: - lines: 350 - statements: 135 - depguard: - list-type: blacklist - packages: - # logging is allowed only by logutils.Log, logrus - # is allowed to use only in logutils package - - github.com/sirupsen/logrus - dupl: - threshold: 100 - errorlint: - # Check whether fmt.Errorf uses the %w verb for formatting errors. - # See the https://github.com/polyfloyd/go-errorlint for caveats. - # Default: true - errorf: false - # Permit more than 1 %w verb, valid per Go 1.20 (Requires errorf:true) - # Default: true - errorf-multi: true - # Check for plain type assertions and type switches. - # Default: true - asserts: true - # Check for plain error comparisons. - # Default: true - comparison: true - exhaustive: - # Program elements to check for exhaustiveness. - # Default: [ switch ] - check: - - switch - - map - # Check switch statements in generated files also. - # Default: false - check-generated: true - # Presence of "default" case in switch statements satisfies exhaustiveness, - # even if all enum members are not listed. - # Default: false - default-signifies-exhaustive: true - # Enum members matching the supplied regex do not have to be listed in - # switch statements to satisfy exhaustiveness. - # Default: "" - ignore-enum-members: "Example.+" - # Enum types matching the supplied regex do not have to be listed in - # switch statements to satisfy exhaustiveness. - # Default: "" - ignore-enum-types: "Example.+" - # Consider enums only in package scopes, not in inner scopes. - # Default: false - package-scope-only: true - # Only run exhaustive check on switches with "//exhaustive:enforce" comment. - # Default: false - explicit-exhaustive-switch: false - # Only run exhaustive check on map literals with "//exhaustive:enforce" comment. - # Default: false - explicit-exhaustive-map: false - gci: - local-prefixes: github.com/datatrails/go-datatrails-merklelog - goconst: - min-len: 2 - min-occurrences: 2 - gocritic: - enabled-tags: - - performance - - style - - experimental - disabled-checks: - - wrapperFunc - gocognit: - min-complexity: 75 - gocyclo: - min-complexity: 10 - goimports: - local-prefixes: github.com/golangci/golangci-lint - golint: - min-confidence: 0 - govet: - check-shadowing: true - settings: - printf: - funcs: - - Infof - - Warnf - - Errorf - - Fatalf - lll: - line-length: 500 - maligned: - suggest-new: true - misspell: - locale: UK +run: + timeout: 5m + build-tags: "golangcilint unit integration e2e azurite" -# depguard (control upstream repos) not needed -# dupl - see ticket #3095 -# funlen - it is to anoying for test code and this sort of subjective judgement is what PR reviews are for -# exhaustive - see ticket #3096 -# gci - disabled as confusing and not really useful -# gochecknoglobals - not really useful -# goconst - see ticket #3097 -# goerr113 - disabled see https://github.com/Djarvur/go-err113/issues/10 -# gofumpt - not useful - confusing messages -# gomnd - see ticket #3116 -# govet - see ticket #3117 -# nilreturn onwardis not yet evaluated... -# maligned - this guards against performance issues due to accessing -# mis-aligned structs. We don't have direct evidence of this being a -# real problem for us. We use a lot of generated code in our hot -# paths anyway (we have no control over there layout). Until we get -# direct evidence this is hurting us, we prefer our stucts layed out -# logically and don't want to have to nolint tag everything. -# -# misspell - expected UK spelling with misspell, but customer facing text needs to be US. -# tagalign - suppress until we can get a golang code formatter that will fix this (cosmetic) -# -# WARN: typecheck cannot be disabled as golang-ci uses it internally to detect uncompilable code. -# Unfortunately the src/azb2c package triggers this erroneously so we add it to skip-dirs below. -# linters: enable-all: true disable: @@ -132,8 +19,7 @@ linters: - exhaustruct - forbidigo - forcetypeassert - # DONT re-enable funlen please - - funlen + - funlen # DONT re-enable funlen please - gci - gochecknoglobals - goconst @@ -192,9 +78,82 @@ linters: - wsl - wrapcheck -run: - build-tags: - - golangcilint +linters-settings: + funlen: + lines: 350 + statements: 135 + + depguard: + list-type: blacklist + packages: + - github.com/sirupsen/logrus # only allowed in logutils + + dupl: + threshold: 100 + + errorlint: + errorf: false + errorf-multi: true + asserts: true + comparison: true + + exhaustive: + check: + - switch + - map + check-generated: true + default-signifies-exhaustive: true + ignore-enum-members: "Example.+" + ignore-enum-types: "Example.+" + package-scope-only: true + explicit-exhaustive-switch: false + explicit-exhaustive-map: false + + gci: + local-prefixes: github.com/datatrails/go-datatrails-merklelog + + goconst: + min-len: 2 + min-occurrences: 2 + + gocritic: + enabled-tags: + - performance + - style + - experimental + disabled-checks: + - wrapperFunc + + gocognit: + min-complexity: 75 + + gocyclo: + min-complexity: 10 + + goimports: + local-prefixes: github.com/golangci/golangci-lint + + golint: + min-confidence: 0 + + govet: + check-shadowing: true + settings: + printf: + funcs: + - Infof + - Warnf + - Errorf + - Fatalf + + lll: + line-length: 500 + + maligned: + suggest-new: true + + misspell: + locale: UK issues: exclude-rules: diff --git a/amourystatement.go b/amourystatement.go new file mode 100644 index 0000000..ae71001 --- /dev/null +++ b/amourystatement.go @@ -0,0 +1,467 @@ +package veracity + +var AmourySignedStatement = []byte{ + 0xd2, 0x84, 0x59, 0x13, 0xf2, 0xa6, 0x01, 0x38, 0x25, 0x0f, 0xa4, 0x01, + 0x78, 0x5c, 0x64, 0x69, 0x64, 0x3a, 0x78, 0x35, 0x30, 0x39, 0x3a, 0x30, + 0x3a, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x3a, 0x49, 0x5f, 0x5f, 0x69, + 0x75, 0x4c, 0x32, 0x35, 0x6f, 0x58, 0x45, 0x56, 0x46, 0x64, 0x54, 0x50, + 0x5f, 0x61, 0x42, 0x4c, 0x78, 0x5f, 0x65, 0x54, 0x31, 0x52, 0x50, 0x48, + 0x62, 0x43, 0x51, 0x5f, 0x45, 0x43, 0x42, 0x51, 0x66, 0x59, 0x5a, 0x70, + 0x74, 0x39, 0x73, 0x3a, 0x3a, 0x65, 0x6b, 0x75, 0x3a, 0x31, 0x2e, 0x33, + 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x33, 0x31, 0x31, + 0x2e, 0x37, 0x36, 0x2e, 0x35, 0x39, 0x2e, 0x31, 0x2e, 0x31, 0x02, 0x78, + 0x26, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, + 0x6c, 0x2f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2f, + 0x70, 0x68, 0x69, 0x2d, 0x34, 0x2d, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x69, 0x6e, 0x67, 0x06, 0xc1, 0x1a, 0x68, 0x54, 0x89, 0xb3, 0x63, 0x73, + 0x76, 0x6e, 0x00, 0x18, 0x21, 0x83, 0x59, 0x06, 0x78, 0x30, 0x82, 0x06, + 0x74, 0x30, 0x82, 0x04, 0x5c, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x13, + 0x33, 0x00, 0x00, 0x00, 0x47, 0xa0, 0xab, 0xc0, 0xe5, 0xbd, 0x99, 0x39, + 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c, 0x05, 0x00, 0x30, 0x55, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, + 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, + 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x26, 0x30, + 0x24, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1d, 0x4d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x53, 0x43, 0x44, 0x20, 0x50, 0x72, + 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x20, 0x52, 0x53, 0x41, 0x20, 0x43, + 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x32, 0x32, 0x30, 0x32, + 0x30, 0x34, 0x35, 0x34, 0x36, 0x5a, 0x17, 0x0d, 0x32, 0x36, 0x30, 0x32, + 0x31, 0x38, 0x32, 0x30, 0x34, 0x35, 0x34, 0x36, 0x5a, 0x30, 0x81, 0x81, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x52, 0x65, 0x64, + 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, + 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x22, 0x4d, + 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x53, 0x43, 0x44, + 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x20, 0x52, 0x53, + 0x41, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x30, 0x82, 0x01, + 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x8f, 0x00, 0x30, 0x82, 0x01, + 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb6, 0x9a, 0x2e, 0xa4, 0xba, 0xce, + 0xfe, 0xaf, 0x9a, 0xc5, 0x63, 0xd2, 0xa9, 0x5d, 0x14, 0x69, 0xc9, 0x39, + 0x5f, 0xdb, 0x0a, 0x23, 0xad, 0xc7, 0x3f, 0x7c, 0x6e, 0x94, 0x50, 0x71, + 0x32, 0xea, 0xe6, 0xef, 0x33, 0x90, 0x58, 0x5c, 0xb6, 0xf6, 0x6f, 0xbd, + 0x70, 0x84, 0x49, 0x33, 0x35, 0xa4, 0xa9, 0x58, 0x02, 0xb8, 0xad, 0x37, + 0xb5, 0xa4, 0x13, 0x3f, 0x9e, 0xc0, 0x54, 0x2a, 0x83, 0x47, 0xf7, 0xa3, + 0xe3, 0xa6, 0x30, 0x46, 0x9d, 0x88, 0x42, 0xde, 0x4f, 0x2c, 0xea, 0x6e, + 0x4b, 0xbf, 0x96, 0x5e, 0x68, 0x36, 0xf8, 0x0b, 0x11, 0x31, 0x24, 0xed, + 0x8c, 0x75, 0x17, 0xd0, 0x27, 0x37, 0x49, 0x11, 0x9a, 0x37, 0x92, 0x2b, + 0xda, 0xc7, 0x5a, 0x6a, 0x6d, 0xc1, 0xdb, 0xec, 0x23, 0x54, 0x47, 0xd3, + 0x6e, 0x55, 0x64, 0x87, 0x3a, 0xf5, 0x64, 0x87, 0x3f, 0x84, 0x18, 0x99, + 0x91, 0x1e, 0x28, 0x75, 0x0b, 0x57, 0xe5, 0xfa, 0xf5, 0x1f, 0xfc, 0x52, + 0x1f, 0x79, 0x20, 0x6a, 0x9c, 0x0a, 0x24, 0x2b, 0xac, 0xf1, 0x7a, 0x7a, + 0x7f, 0xdc, 0x08, 0xa0, 0x33, 0x7b, 0x93, 0x6d, 0x14, 0x18, 0x38, 0x3b, + 0xa6, 0xe1, 0xee, 0xa8, 0x71, 0x2e, 0x81, 0x86, 0x2c, 0x69, 0x92, 0xc5, + 0x80, 0x27, 0x82, 0xb1, 0xb2, 0x80, 0xdc, 0x62, 0x86, 0x1d, 0xa4, 0x01, + 0x56, 0x3e, 0x08, 0x3e, 0x6b, 0xd5, 0x1a, 0x7a, 0x42, 0xd5, 0x74, 0x21, + 0x5a, 0x43, 0x39, 0x5d, 0x69, 0x90, 0x44, 0x77, 0x57, 0x99, 0xbf, 0x3a, + 0x21, 0x66, 0x87, 0xbc, 0xca, 0x86, 0x45, 0xa5, 0xc3, 0x38, 0xf3, 0xe4, + 0x42, 0xc7, 0xa5, 0x8d, 0x92, 0xa9, 0xc0, 0x14, 0x69, 0xc0, 0xa1, 0x2d, + 0xcc, 0x28, 0x43, 0xb5, 0xd7, 0x2b, 0x9e, 0xd4, 0xe2, 0x8b, 0x96, 0x71, + 0x0f, 0x6c, 0xff, 0xcb, 0xc8, 0x96, 0xcc, 0x35, 0x37, 0x5a, 0x79, 0x1a, + 0x2b, 0x2a, 0x45, 0xc5, 0xc3, 0x26, 0x5f, 0x03, 0x25, 0xe4, 0xdf, 0xd4, + 0xf7, 0xec, 0x1a, 0x30, 0xc1, 0xbe, 0xa0, 0xa5, 0x76, 0xa5, 0x02, 0x98, + 0xc0, 0x60, 0x0b, 0x34, 0x9b, 0x9f, 0xd7, 0x47, 0xe8, 0x92, 0xf1, 0xa5, + 0xa9, 0xeb, 0x03, 0x4f, 0x33, 0x9d, 0x54, 0x5d, 0x47, 0xde, 0xcc, 0x2e, + 0x02, 0xfa, 0x6b, 0xe2, 0x1a, 0x25, 0x79, 0x38, 0x44, 0xd7, 0x68, 0x91, + 0xe3, 0x2d, 0x60, 0x33, 0x80, 0x8b, 0x7c, 0x56, 0x5a, 0xf9, 0x49, 0x0c, + 0x94, 0x2d, 0x83, 0x3e, 0x51, 0x04, 0xef, 0xf3, 0x73, 0x42, 0x13, 0x0c, + 0xc6, 0x31, 0xf1, 0xb6, 0x6c, 0x4f, 0xb2, 0x0b, 0x0f, 0x1d, 0xd7, 0xfe, + 0x33, 0x3b, 0x77, 0x75, 0xa6, 0x6f, 0x1a, 0x35, 0x49, 0x08, 0x2c, 0x3c, + 0x30, 0xe2, 0x70, 0x32, 0xc8, 0x69, 0x3f, 0xb4, 0xf5, 0xb6, 0xdc, 0xe5, + 0x34, 0xf2, 0x6e, 0xa4, 0xf7, 0x73, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x82, 0x01, 0x8e, 0x30, 0x82, 0x01, 0x8a, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, + 0x16, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0f, 0x30, 0x0d, 0x06, 0x0b, + 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x4c, 0x3b, 0x01, 0x01, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf3, 0x16, + 0x75, 0x55, 0x53, 0x35, 0x76, 0x3f, 0x69, 0xbe, 0xef, 0x1f, 0xd4, 0xab, + 0x22, 0x89, 0x1f, 0x7f, 0x9e, 0x18, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d, + 0x11, 0x04, 0x3e, 0x30, 0x3c, 0xa4, 0x3a, 0x30, 0x38, 0x31, 0x1e, 0x30, + 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, + 0x04, 0x05, 0x13, 0x0d, 0x34, 0x36, 0x39, 0x34, 0x35, 0x31, 0x2b, 0x35, + 0x30, 0x33, 0x37, 0x39, 0x30, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, + 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x55, 0xcd, 0x4d, 0x85, 0x6e, 0xcd, + 0x4a, 0x35, 0xc3, 0x8e, 0x3f, 0x72, 0x01, 0xba, 0xaa, 0x98, 0x19, 0x97, + 0x4b, 0xa7, 0x30, 0x5e, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x57, 0x30, + 0x55, 0x30, 0x53, 0xa0, 0x51, 0xa0, 0x4f, 0x86, 0x4d, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, + 0x69, 0x6f, 0x70, 0x73, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x4d, 0x69, 0x63, + 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x25, 0x32, 0x30, 0x53, 0x43, 0x44, + 0x25, 0x32, 0x30, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x25, + 0x32, 0x30, 0x52, 0x53, 0x41, 0x25, 0x32, 0x30, 0x43, 0x41, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x6b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x01, 0x04, 0x5f, 0x30, 0x5d, 0x30, 0x5b, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x4f, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, + 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, + 0x6f, 0x70, 0x73, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x4d, 0x69, + 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x25, 0x32, 0x30, 0x53, 0x43, + 0x44, 0x25, 0x32, 0x30, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, + 0x25, 0x32, 0x30, 0x52, 0x53, 0x41, 0x25, 0x32, 0x30, 0x43, 0x41, 0x2e, + 0x63, 0x72, 0x74, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, + 0x00, 0x8c, 0x4f, 0x3c, 0xdf, 0x9b, 0x0c, 0x36, 0x09, 0xfb, 0x6a, 0x31, + 0x7a, 0x7a, 0xa5, 0xf2, 0x36, 0x40, 0x67, 0x29, 0x4b, 0xec, 0xfc, 0x85, + 0xa8, 0x4d, 0xc6, 0xda, 0x46, 0x40, 0x1d, 0x7e, 0x92, 0x17, 0x3b, 0xfb, + 0x84, 0x6c, 0xcc, 0xd0, 0x4a, 0x14, 0x21, 0xf4, 0xd2, 0x5a, 0xa8, 0x44, + 0x94, 0xbe, 0x75, 0x82, 0x9e, 0x77, 0x2e, 0x74, 0x3c, 0x5f, 0xe6, 0x2b, + 0xd0, 0x9d, 0x07, 0xe4, 0x3b, 0xc5, 0x43, 0xec, 0x47, 0x7c, 0xfa, 0xaa, + 0x32, 0xd4, 0x1e, 0xd7, 0x0b, 0xf3, 0xb6, 0xb5, 0xd8, 0x12, 0x29, 0x76, + 0xa8, 0x74, 0x8d, 0xd4, 0x4c, 0xc2, 0xb3, 0x03, 0xce, 0x67, 0x43, 0x02, + 0x0b, 0xf2, 0x23, 0x77, 0x99, 0x3f, 0xa8, 0x20, 0x62, 0x79, 0xc4, 0xd3, + 0xbd, 0x40, 0x64, 0x91, 0x93, 0x6c, 0x74, 0xe5, 0xd8, 0xa4, 0x28, 0x34, + 0x1b, 0xf5, 0xe8, 0x10, 0xb3, 0xaa, 0xa1, 0x64, 0x09, 0xef, 0x72, 0xaf, + 0x6d, 0xfb, 0xce, 0x0e, 0x91, 0xe2, 0x7e, 0x8c, 0xc8, 0x28, 0x8a, 0x2f, + 0x3e, 0xe6, 0x89, 0x7d, 0x8a, 0x5f, 0xf9, 0x5e, 0x54, 0xb0, 0xf0, 0xc9, + 0x8e, 0x0c, 0xfc, 0x0d, 0x8b, 0xb4, 0x6c, 0x52, 0x12, 0x8c, 0x90, 0x94, + 0x22, 0x9b, 0x04, 0x80, 0x38, 0xad, 0xf7, 0x41, 0x18, 0x2c, 0x12, 0xe9, + 0x7a, 0x05, 0xba, 0x2d, 0x77, 0xf2, 0xc2, 0x96, 0xd8, 0x61, 0x8c, 0xd0, + 0x99, 0x47, 0xd7, 0xee, 0x1e, 0xb3, 0x42, 0x31, 0xda, 0x46, 0x1d, 0x9b, + 0x29, 0xfe, 0x36, 0x54, 0xe9, 0xa9, 0xd4, 0xc6, 0x7b, 0x8c, 0xb4, 0x21, + 0x48, 0xbd, 0x93, 0x50, 0xa3, 0x91, 0x33, 0x63, 0x67, 0x03, 0xbe, 0xe2, + 0x68, 0x93, 0x30, 0x5c, 0xda, 0x22, 0xbb, 0x80, 0xd7, 0xc0, 0x9c, 0x4b, + 0xf8, 0x4e, 0xb1, 0x3a, 0x79, 0x2a, 0x57, 0x67, 0xb5, 0x1e, 0xd0, 0xba, + 0xd7, 0x79, 0x6d, 0x2e, 0xf1, 0x7d, 0x9c, 0x9b, 0x43, 0xdd, 0xf2, 0x21, + 0x05, 0xb1, 0x59, 0x28, 0xdf, 0x7a, 0x3b, 0x5c, 0x46, 0x3f, 0x29, 0x33, + 0xf1, 0x28, 0x77, 0x85, 0xfb, 0x75, 0x5e, 0x89, 0xea, 0xbf, 0xe5, 0x12, + 0xe8, 0x29, 0x67, 0xb1, 0x06, 0x48, 0xd5, 0xb2, 0xf0, 0x78, 0xc4, 0xed, + 0x87, 0x9e, 0x71, 0x88, 0x32, 0x05, 0xf6, 0x1d, 0x34, 0x44, 0x4d, 0x26, + 0x01, 0xf4, 0xf6, 0x19, 0x83, 0x1d, 0x01, 0xc1, 0xa6, 0x80, 0xa2, 0x81, + 0x2e, 0x3a, 0x13, 0x49, 0xbd, 0xea, 0x8f, 0x2e, 0x08, 0x2f, 0xf2, 0x4f, + 0x69, 0xa9, 0x4b, 0x3e, 0x37, 0xcb, 0xc5, 0xb8, 0x19, 0x00, 0xa4, 0xab, + 0x9e, 0x61, 0xfc, 0x35, 0x8b, 0xd8, 0xba, 0xf4, 0x3a, 0x19, 0xab, 0xff, + 0x6f, 0x2a, 0x0a, 0x21, 0x37, 0x1e, 0x37, 0x52, 0x0b, 0xdc, 0x5a, 0x88, + 0x49, 0x5b, 0x8a, 0xea, 0x7d, 0xd4, 0x88, 0x50, 0x28, 0xaa, 0xb9, 0xad, + 0x3f, 0x90, 0x5f, 0x16, 0xd7, 0xe7, 0x9f, 0x21, 0xfe, 0x8a, 0x8c, 0x42, + 0x70, 0xdf, 0x2d, 0xc5, 0x83, 0x04, 0xb6, 0x96, 0xd6, 0x69, 0xff, 0x7b, + 0x6e, 0x30, 0xcd, 0xc2, 0xa0, 0x9b, 0xe4, 0xb0, 0xf4, 0x4a, 0x45, 0xdc, + 0x03, 0xea, 0xf2, 0x17, 0x90, 0xb8, 0x5f, 0x58, 0x97, 0x9d, 0x4f, 0x23, + 0xd9, 0xee, 0x4f, 0x29, 0x6d, 0x80, 0x4c, 0x63, 0x71, 0xdf, 0x20, 0x78, + 0x8c, 0xfd, 0x6b, 0x1b, 0x63, 0x48, 0xcd, 0xaa, 0xb2, 0x4f, 0x4b, 0x1f, + 0x3d, 0x94, 0x1b, 0xd9, 0xa0, 0x7f, 0xf2, 0x2e, 0xb0, 0xe1, 0xc0, 0xa9, + 0x52, 0x4f, 0xe6, 0xe3, 0x56, 0xb7, 0xed, 0xd0, 0x49, 0xd9, 0x91, 0x67, + 0x6a, 0xab, 0x6b, 0x8e, 0xca, 0xce, 0x65, 0xc2, 0x5b, 0xe4, 0xea, 0x12, + 0xf2, 0x9c, 0x26, 0xe4, 0xd6, 0xb3, 0xc8, 0xe1, 0xd2, 0xe3, 0x39, 0x4d, + 0xc1, 0x22, 0x50, 0x37, 0x2c, 0x69, 0x1b, 0xa3, 0xe5, 0x59, 0x06, 0xd5, + 0x30, 0x82, 0x06, 0xd1, 0x30, 0x82, 0x04, 0xb9, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x13, 0x33, 0x00, 0x00, 0x00, 0x03, 0x95, 0x84, 0x47, 0xff, + 0x89, 0xe8, 0x66, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c, 0x05, + 0x00, 0x30, 0x5f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, + 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x27, 0x4d, + 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x53, 0x75, 0x70, + 0x70, 0x6c, 0x79, 0x20, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x52, 0x53, + 0x41, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x32, 0x30, + 0x32, 0x32, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x32, 0x31, 0x37, + 0x30, 0x30, 0x34, 0x35, 0x32, 0x33, 0x5a, 0x17, 0x0d, 0x34, 0x32, 0x30, + 0x32, 0x31, 0x37, 0x30, 0x30, 0x35, 0x35, 0x32, 0x33, 0x5a, 0x30, 0x55, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, + 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, + 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x26, 0x30, + 0x24, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1d, 0x4d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x53, 0x43, 0x44, 0x20, 0x50, 0x72, + 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x20, 0x52, 0x53, 0x41, 0x20, 0x43, + 0x41, 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, + 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xab, 0xed, + 0x7f, 0xb5, 0x71, 0xbe, 0x8c, 0x73, 0xbe, 0xf1, 0xd7, 0xca, 0x9d, 0xf1, + 0x01, 0xd6, 0x74, 0x87, 0xbc, 0x8c, 0x42, 0x93, 0x4c, 0x9f, 0xca, 0x95, + 0x74, 0x6b, 0x4e, 0x47, 0xea, 0x72, 0x84, 0xb5, 0xa4, 0x16, 0xc1, 0x8c, + 0x42, 0x54, 0xd7, 0x0d, 0xed, 0x98, 0x7a, 0xcf, 0xa8, 0xee, 0x60, 0xb4, + 0x20, 0x44, 0x09, 0x33, 0x3d, 0xfd, 0x08, 0x9c, 0x48, 0x8c, 0x6e, 0x97, + 0x60, 0x84, 0x1f, 0x70, 0x5d, 0x82, 0x68, 0xef, 0xfa, 0x30, 0x2c, 0xd6, + 0xcf, 0x2b, 0x1b, 0x16, 0xf9, 0x53, 0x92, 0x86, 0x3f, 0x2c, 0xdf, 0xe0, + 0xd3, 0xf4, 0x65, 0x70, 0x9f, 0xc8, 0x74, 0x59, 0x5f, 0xd1, 0x55, 0x9c, + 0xbe, 0xe8, 0xe9, 0x64, 0xf8, 0x7d, 0x08, 0xb9, 0x44, 0x77, 0x41, 0xd2, + 0xf6, 0xa6, 0x05, 0x44, 0x03, 0xd1, 0x45, 0x03, 0xaf, 0xc1, 0xed, 0xcd, + 0x4f, 0x9b, 0x84, 0x77, 0x7f, 0x1f, 0x45, 0xb2, 0x9b, 0x67, 0xab, 0xc2, + 0x24, 0x6d, 0x9c, 0xfd, 0x8c, 0x47, 0x07, 0x22, 0x9b, 0x7a, 0x8a, 0x18, + 0x45, 0xea, 0x2f, 0x3e, 0x83, 0x69, 0x56, 0x9c, 0x5d, 0x68, 0x80, 0xd2, + 0xeb, 0x82, 0x1d, 0x80, 0x69, 0x7c, 0x99, 0x7f, 0xb2, 0x4c, 0xfc, 0x30, + 0xc0, 0xb1, 0xce, 0x7d, 0x1f, 0x84, 0xd9, 0x45, 0xa0, 0x9e, 0x74, 0x2a, + 0x80, 0xd6, 0x29, 0xd2, 0x10, 0x8c, 0xd9, 0x86, 0x7e, 0x27, 0x9c, 0xd4, + 0xd1, 0x06, 0x42, 0xc1, 0x9d, 0x49, 0x30, 0xb5, 0xd0, 0xf5, 0xe2, 0xb4, + 0xb0, 0x95, 0xb7, 0xb8, 0xf7, 0xe3, 0xee, 0x20, 0x3f, 0x93, 0x59, 0x39, + 0xee, 0x43, 0x77, 0x75, 0x26, 0x78, 0x3f, 0x88, 0x64, 0xa8, 0x65, 0x53, + 0x02, 0x7a, 0xc1, 0xcd, 0xaa, 0x19, 0xb0, 0x83, 0x4c, 0x90, 0x65, 0x49, + 0x6e, 0x01, 0x29, 0x7d, 0x23, 0xeb, 0x44, 0xb0, 0x4e, 0x92, 0xbe, 0x19, + 0x9a, 0x1e, 0xe6, 0xf0, 0xf8, 0xa0, 0x2f, 0xc0, 0x7c, 0xc4, 0x82, 0x74, + 0xd5, 0x3c, 0x75, 0x28, 0x19, 0x9f, 0x89, 0x60, 0x05, 0x1a, 0x65, 0x71, + 0xfb, 0xe3, 0x52, 0x63, 0xca, 0x05, 0xc5, 0x15, 0xbf, 0x0d, 0xd2, 0x9d, + 0xc1, 0x62, 0xeb, 0xe6, 0xcb, 0x82, 0xa4, 0x1d, 0x8e, 0x36, 0x31, 0x7b, + 0x2c, 0xdb, 0xf8, 0x03, 0x9b, 0xf8, 0x49, 0x9f, 0xb3, 0x60, 0x2c, 0x29, + 0x4d, 0xcf, 0x28, 0xbb, 0x13, 0xcf, 0x52, 0xd6, 0x52, 0x1b, 0xf7, 0xe4, + 0x95, 0x51, 0x05, 0xbd, 0xe5, 0xb7, 0xd2, 0x33, 0x09, 0xc1, 0x00, 0x1f, + 0xdb, 0xd5, 0xfc, 0xc0, 0x0b, 0x89, 0xd2, 0x9c, 0x2e, 0x59, 0xa3, 0xf6, + 0x3f, 0x38, 0x90, 0x4a, 0x89, 0xd1, 0xe1, 0x59, 0x91, 0x3f, 0x77, 0x0a, + 0xcf, 0xcf, 0x1a, 0x01, 0xb9, 0xb4, 0xce, 0x6c, 0xef, 0xc7, 0xea, 0x5d, + 0x4c, 0x25, 0xfd, 0x7c, 0x7f, 0xdc, 0x4e, 0xe6, 0x30, 0x12, 0xb8, 0xc9, + 0x03, 0x77, 0x7d, 0x1b, 0xbf, 0xf7, 0xb0, 0x31, 0x84, 0xfd, 0x00, 0x6a, + 0x92, 0x30, 0xbe, 0x36, 0x46, 0x48, 0xf1, 0x70, 0x9d, 0x9b, 0xa5, 0x2b, + 0xf1, 0x02, 0x0a, 0xe0, 0xb6, 0x99, 0x27, 0xf6, 0x41, 0x4f, 0xd4, 0x04, + 0x91, 0x71, 0x7b, 0xc4, 0xc2, 0xf4, 0x14, 0x17, 0xb7, 0x60, 0xb6, 0x16, + 0x93, 0x91, 0x76, 0xa5, 0xce, 0x1d, 0xdb, 0x02, 0x62, 0x9d, 0x92, 0x05, + 0xbc, 0x92, 0x6f, 0x2e, 0xf9, 0x00, 0xa7, 0xff, 0xe0, 0xb9, 0xa6, 0xed, + 0xeb, 0x00, 0x97, 0x4e, 0x3c, 0x47, 0x0f, 0x3d, 0x91, 0x92, 0x22, 0x67, + 0x9a, 0x2b, 0x5e, 0x48, 0xb3, 0xb4, 0xf4, 0x37, 0x90, 0x22, 0xf8, 0x04, + 0x19, 0x8e, 0xe7, 0x25, 0x32, 0xb2, 0xd6, 0x83, 0x30, 0x46, 0x86, 0xa5, + 0x1a, 0xb2, 0xf5, 0xe1, 0x80, 0xf8, 0x43, 0x23, 0x5a, 0xc1, 0xc6, 0xb3, + 0x06, 0xd1, 0x99, 0x43, 0x6d, 0x0d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x82, 0x01, 0x8e, 0x30, 0x82, 0x01, 0x8a, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, + 0x10, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01, + 0x04, 0x03, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0x55, 0xcd, 0x4d, 0x85, 0x6e, 0xcd, 0x4a, 0x35, + 0xc3, 0x8e, 0x3f, 0x72, 0x01, 0xba, 0xaa, 0x98, 0x19, 0x97, 0x4b, 0xa7, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30, 0x08, 0x30, + 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x19, 0x06, 0x09, 0x2b, + 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x04, 0x0c, 0x1e, 0x0a, + 0x00, 0x53, 0x00, 0x75, 0x00, 0x62, 0x00, 0x43, 0x00, 0x41, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, + 0x01, 0x01, 0xff, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, + 0x30, 0x16, 0x80, 0x14, 0x0b, 0xb3, 0x68, 0x3b, 0xaf, 0xda, 0xaf, 0xee, + 0x70, 0xa5, 0x76, 0xd9, 0x21, 0xf7, 0xcc, 0x44, 0x16, 0x07, 0xd0, 0xf8, + 0x30, 0x6c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x65, 0x30, 0x63, 0x30, + 0x61, 0xa0, 0x5f, 0xa0, 0x5d, 0x86, 0x5b, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, + 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x6f, + 0x70, 0x73, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x4d, 0x69, 0x63, 0x72, 0x6f, + 0x73, 0x6f, 0x66, 0x74, 0x25, 0x32, 0x30, 0x53, 0x75, 0x70, 0x70, 0x6c, + 0x79, 0x25, 0x32, 0x30, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x25, 0x32, 0x30, + 0x52, 0x53, 0x41, 0x25, 0x32, 0x30, 0x52, 0x6f, 0x6f, 0x74, 0x25, 0x32, + 0x30, 0x43, 0x41, 0x25, 0x32, 0x30, 0x32, 0x30, 0x32, 0x32, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x79, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x01, 0x04, 0x6d, 0x30, 0x6b, 0x30, 0x69, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x5d, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, + 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, + 0x6f, 0x70, 0x73, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x4d, 0x69, + 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x25, 0x32, 0x30, 0x53, 0x75, + 0x70, 0x70, 0x6c, 0x79, 0x25, 0x32, 0x30, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x25, 0x32, 0x30, 0x52, 0x53, 0x41, 0x25, 0x32, 0x30, 0x52, 0x6f, 0x6f, + 0x74, 0x25, 0x32, 0x30, 0x43, 0x41, 0x25, 0x32, 0x30, 0x32, 0x30, 0x32, + 0x32, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, + 0x00, 0x6f, 0xde, 0x61, 0xd6, 0x6b, 0xfa, 0x41, 0xbf, 0x6d, 0x1c, 0x94, + 0xc8, 0xe1, 0x8a, 0xc3, 0xca, 0xa7, 0xf4, 0x33, 0x04, 0xe4, 0x29, 0x26, + 0xcf, 0x7b, 0xe6, 0x21, 0xd7, 0x26, 0x75, 0x4f, 0x8b, 0x13, 0x74, 0xe7, + 0x17, 0x31, 0x01, 0x46, 0x8d, 0x45, 0x44, 0x5d, 0x6d, 0x74, 0xe7, 0x6a, + 0x0a, 0xae, 0x7c, 0xbe, 0xd1, 0xf9, 0x96, 0xec, 0x5a, 0xf2, 0x19, 0x25, + 0xe3, 0x0c, 0xaf, 0xbc, 0x08, 0xef, 0xd1, 0xa8, 0x69, 0xa6, 0xbf, 0xb6, + 0x50, 0x8e, 0xfd, 0xbf, 0x2a, 0x33, 0x28, 0x62, 0x02, 0xe2, 0xe7, 0x76, + 0xcc, 0x1a, 0x56, 0x82, 0xd9, 0xb1, 0x89, 0xf1, 0x6f, 0xe4, 0xac, 0x97, + 0xcb, 0xb9, 0x19, 0xca, 0xbb, 0xee, 0x69, 0x50, 0xe6, 0x47, 0x78, 0x70, + 0x02, 0x1a, 0x59, 0xc9, 0x37, 0xd2, 0xe9, 0x72, 0xf1, 0x75, 0x19, 0xec, + 0x0e, 0x5b, 0x03, 0xf7, 0x9a, 0x9d, 0xc3, 0xcf, 0x61, 0x04, 0xa7, 0xfc, + 0x97, 0xf4, 0x1f, 0x16, 0x10, 0xa4, 0x3c, 0x98, 0xb7, 0x04, 0xf7, 0xed, + 0x6f, 0x41, 0x35, 0x90, 0x54, 0x39, 0xa9, 0x4c, 0xe5, 0xe2, 0x34, 0xa7, + 0x80, 0x22, 0xb2, 0x4f, 0xc7, 0xdd, 0x5d, 0x90, 0x51, 0x74, 0x79, 0x47, + 0x8a, 0x5d, 0x75, 0x04, 0x9a, 0x4d, 0x9b, 0xb8, 0x1c, 0x27, 0x12, 0x50, + 0x7d, 0x85, 0x81, 0x5f, 0xe1, 0x03, 0x46, 0x93, 0x46, 0x4b, 0x46, 0x08, + 0xe7, 0xf7, 0x10, 0x84, 0xc1, 0x12, 0xdf, 0x98, 0xd8, 0x25, 0xf1, 0x86, + 0xa2, 0xcc, 0x3d, 0x0c, 0x50, 0x9f, 0x39, 0x1c, 0xe3, 0x46, 0x67, 0x35, + 0xce, 0x91, 0x15, 0x5d, 0x4a, 0xe7, 0x6e, 0x72, 0x43, 0xb5, 0xc8, 0xeb, + 0xa5, 0xe4, 0x33, 0xd0, 0x34, 0x20, 0xe8, 0xa5, 0x70, 0x3a, 0x34, 0xa4, + 0x12, 0x4b, 0xe3, 0xcc, 0xa9, 0x6d, 0x1f, 0x4f, 0x9b, 0x9d, 0x4d, 0x48, + 0x2a, 0xfa, 0xd2, 0xb9, 0x5c, 0xbc, 0x44, 0x55, 0x9c, 0x8b, 0x5b, 0xdd, + 0xac, 0x08, 0xf4, 0x23, 0xa6, 0x36, 0x25, 0xa0, 0x0b, 0x70, 0x4d, 0x34, + 0x2e, 0x1f, 0x3a, 0x04, 0x71, 0x98, 0x54, 0xaf, 0xcd, 0x64, 0x46, 0x50, + 0x00, 0x05, 0xe5, 0x08, 0xf4, 0x5a, 0x39, 0x09, 0x1c, 0x09, 0xac, 0x64, + 0xb9, 0x3d, 0x33, 0x35, 0x90, 0x74, 0x36, 0x9a, 0x54, 0xd7, 0x8f, 0x39, + 0x8c, 0x74, 0x7a, 0xee, 0x9e, 0xfc, 0x6d, 0xb0, 0x69, 0x5d, 0x27, 0xbd, + 0x2f, 0x27, 0xe9, 0x58, 0x5c, 0x01, 0xde, 0xae, 0xa3, 0xc9, 0xef, 0x4a, + 0x5b, 0x6b, 0x97, 0x8b, 0xfe, 0xf3, 0x4c, 0xf6, 0x01, 0xc9, 0x7d, 0x00, + 0xb5, 0xea, 0x15, 0xa3, 0xa2, 0x56, 0xe7, 0xa2, 0x57, 0x84, 0x82, 0xc2, + 0x5a, 0x6c, 0xc1, 0x8d, 0xb8, 0xfc, 0x59, 0x4c, 0xdc, 0xa3, 0xfb, 0x31, + 0x8f, 0x06, 0xed, 0x85, 0x3d, 0x16, 0xb4, 0xa0, 0xc0, 0x0c, 0xab, 0x8a, + 0x44, 0x46, 0xa1, 0x0b, 0x2d, 0x2d, 0x49, 0xeb, 0x2d, 0x0f, 0x70, 0xf9, + 0x5d, 0xc1, 0x88, 0x74, 0xcb, 0xd4, 0xf4, 0x10, 0x4b, 0x16, 0x09, 0x57, + 0xb5, 0x6d, 0x8b, 0x99, 0xd4, 0xc3, 0x7b, 0x89, 0x4b, 0x05, 0x2b, 0xae, + 0x4b, 0x64, 0xd0, 0xa0, 0x50, 0x70, 0xfc, 0x1a, 0x2a, 0x5d, 0xcb, 0x42, + 0x7b, 0xfb, 0x03, 0x7a, 0xbe, 0x53, 0x57, 0x17, 0x99, 0xe2, 0x1e, 0xf3, + 0x53, 0x9d, 0x2f, 0x72, 0xb0, 0x95, 0xef, 0x8c, 0x7e, 0xc4, 0x22, 0x38, + 0x5e, 0x95, 0x26, 0x5c, 0x8d, 0xee, 0xc8, 0xba, 0xe1, 0x11, 0x52, 0x61, + 0xd0, 0x2d, 0x37, 0x2f, 0x7a, 0x44, 0xf8, 0xd4, 0xe6, 0x20, 0x89, 0xbe, + 0xed, 0x99, 0x3c, 0xab, 0x93, 0x26, 0xae, 0x44, 0x3b, 0xa5, 0x5c, 0x24, + 0x25, 0xd4, 0xfb, 0x71, 0x6e, 0xd8, 0x82, 0x2a, 0xa4, 0xa0, 0x22, 0x0e, + 0x7b, 0x28, 0x1b, 0xfd, 0x45, 0x4f, 0x5f, 0x18, 0x56, 0x59, 0x05, 0xb3, + 0x30, 0x82, 0x05, 0xaf, 0x30, 0x82, 0x03, 0x97, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x68, 0x28, 0xd5, 0x4c, 0x7e, 0x5c, 0xda, 0xbd, 0x43, + 0x39, 0xae, 0x0c, 0xc1, 0x5a, 0x2a, 0x35, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c, 0x05, 0x00, 0x30, 0x5f, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, + 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, + 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x30, 0x30, + 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x27, 0x4d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x53, 0x75, 0x70, 0x70, 0x6c, 0x79, + 0x20, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x52, 0x53, 0x41, 0x20, 0x52, + 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x32, 0x30, 0x32, 0x32, 0x30, + 0x1e, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x32, 0x31, 0x37, 0x30, 0x30, 0x31, + 0x32, 0x33, 0x36, 0x5a, 0x17, 0x0d, 0x34, 0x37, 0x30, 0x32, 0x31, 0x37, + 0x30, 0x30, 0x32, 0x31, 0x30, 0x39, 0x5a, 0x30, 0x5f, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1e, + 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, + 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x27, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, + 0x66, 0x74, 0x20, 0x53, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x20, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x20, 0x52, 0x53, 0x41, 0x20, 0x52, 0x6f, 0x6f, 0x74, + 0x20, 0x43, 0x41, 0x20, 0x32, 0x30, 0x32, 0x32, 0x30, 0x82, 0x02, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, + 0x02, 0x82, 0x02, 0x01, 0x00, 0x9e, 0x25, 0x01, 0x66, 0x19, 0x1f, 0x64, + 0x34, 0xc1, 0x9d, 0x39, 0x23, 0x62, 0x1d, 0x71, 0x8b, 0x56, 0xea, 0x25, + 0xd2, 0x9b, 0x1f, 0xef, 0x27, 0x01, 0x82, 0xbf, 0x77, 0xd8, 0x94, 0x33, + 0x83, 0x18, 0x48, 0x9b, 0x50, 0x9e, 0x7b, 0x96, 0x64, 0xc2, 0xd0, 0xc3, + 0x5f, 0x45, 0xff, 0x32, 0x9c, 0xe8, 0x17, 0x17, 0xbd, 0x78, 0xed, 0x75, + 0x98, 0x5f, 0x3a, 0x06, 0x08, 0x2b, 0x1c, 0x37, 0x9b, 0x46, 0x4a, 0x90, + 0x0a, 0xb0, 0xaf, 0x46, 0x92, 0x3e, 0x33, 0x89, 0x2a, 0xfa, 0xb8, 0xe7, + 0x32, 0x63, 0xf3, 0x23, 0xc7, 0x6e, 0xd2, 0x14, 0xfb, 0x26, 0x58, 0xee, + 0xfe, 0x06, 0x84, 0x54, 0xfa, 0xc1, 0x1f, 0x37, 0xaa, 0xdb, 0xd4, 0xec, + 0x56, 0x2a, 0xbf, 0x49, 0xbd, 0xcc, 0xeb, 0x02, 0xed, 0xc6, 0x4e, 0xfc, + 0xac, 0x19, 0xb5, 0x12, 0x35, 0x69, 0x15, 0x89, 0x17, 0x4d, 0xa3, 0x68, + 0xea, 0x6c, 0x1e, 0x29, 0x9a, 0x09, 0xf3, 0xce, 0x7a, 0x21, 0xc6, 0x09, + 0xd1, 0x19, 0xea, 0x8f, 0x30, 0x46, 0x69, 0x3b, 0x68, 0x04, 0x2b, 0x7c, + 0x8a, 0x2d, 0xd6, 0x63, 0x5d, 0xea, 0x6d, 0xd6, 0x39, 0x9e, 0xbd, 0x06, + 0x3e, 0x5b, 0xee, 0x2f, 0x11, 0x5b, 0x28, 0x6b, 0xa7, 0x52, 0xa4, 0x68, + 0x5e, 0x4c, 0xa4, 0xea, 0xae, 0xce, 0x23, 0xbf, 0x4c, 0x36, 0x71, 0xda, + 0x81, 0x45, 0x50, 0x8e, 0xca, 0x86, 0xce, 0xff, 0x53, 0xc3, 0xb8, 0x43, + 0xb3, 0x24, 0xee, 0x07, 0x7a, 0xa2, 0xb4, 0xfa, 0xc7, 0x0a, 0x1d, 0x7b, + 0xc6, 0x52, 0x35, 0x31, 0xec, 0x08, 0x1f, 0x84, 0x80, 0x92, 0x5b, 0xf8, + 0xb1, 0xda, 0x39, 0xd6, 0xc9, 0xe7, 0xe5, 0x89, 0x04, 0x7e, 0x51, 0x7f, + 0xf4, 0xe6, 0x6a, 0x64, 0x47, 0x49, 0xea, 0xf8, 0xec, 0xa6, 0xf6, 0xa0, + 0x43, 0x53, 0xfe, 0xda, 0xc3, 0x23, 0x24, 0xd8, 0x25, 0xda, 0x13, 0x2c, + 0x2a, 0xb7, 0x3f, 0x94, 0xde, 0x77, 0x1c, 0x4c, 0x78, 0x1c, 0x6a, 0xf9, + 0x9a, 0x8f, 0xeb, 0x6a, 0x15, 0x77, 0x77, 0xad, 0x49, 0x84, 0xce, 0x10, + 0x40, 0xc7, 0x99, 0x48, 0x0f, 0xd5, 0x96, 0x1e, 0x80, 0x9c, 0x73, 0xa1, + 0x38, 0xa1, 0x03, 0x6f, 0xd3, 0x4d, 0x20, 0xd0, 0xb5, 0x43, 0xe4, 0xf7, + 0x2e, 0x78, 0x0f, 0x4e, 0xf7, 0xbc, 0xbf, 0x65, 0xda, 0x6d, 0x90, 0x0b, + 0x5b, 0xbf, 0xde, 0xea, 0x27, 0x27, 0x99, 0x64, 0xf8, 0x39, 0x7c, 0x73, + 0x3d, 0xd6, 0x21, 0xd2, 0xee, 0xd6, 0xf3, 0x53, 0x11, 0x2e, 0x55, 0xc3, + 0xdc, 0xea, 0xf1, 0x29, 0x57, 0xde, 0x51, 0xa1, 0x78, 0x73, 0x90, 0x0b, + 0x2f, 0xf5, 0xc9, 0x75, 0x36, 0xeb, 0x8d, 0xd2, 0x6d, 0x8e, 0x79, 0x5d, + 0xba, 0x1a, 0x38, 0xff, 0xdf, 0x19, 0x01, 0xa8, 0xd2, 0xc8, 0xd1, 0xd6, + 0xf2, 0xeb, 0x8a, 0xf5, 0x2e, 0xd1, 0xcc, 0x93, 0x13, 0x9b, 0x9c, 0x90, + 0x78, 0x65, 0x63, 0x79, 0x04, 0xc4, 0xf1, 0x9e, 0x9f, 0x8c, 0x3a, 0xf3, + 0x64, 0x0c, 0xfe, 0x98, 0x1d, 0x93, 0xe2, 0x8f, 0x56, 0xa5, 0x63, 0x53, + 0x23, 0xb8, 0x6e, 0x73, 0x16, 0x45, 0x1a, 0xb6, 0xf7, 0x7b, 0x0f, 0xcd, + 0xa4, 0x32, 0xff, 0x5a, 0xfe, 0x96, 0x8d, 0xe1, 0x87, 0x78, 0xdb, 0x70, + 0x83, 0xa8, 0x24, 0x85, 0x69, 0x20, 0xc2, 0x6d, 0x12, 0x0d, 0xe5, 0x79, + 0xf6, 0x2a, 0x59, 0xcf, 0xd6, 0xab, 0xe7, 0x81, 0xe6, 0xa0, 0xb1, 0x88, + 0x2d, 0x08, 0x8c, 0x0b, 0xb1, 0xcf, 0xd7, 0x6c, 0x36, 0xaf, 0x9e, 0xf9, + 0x03, 0x67, 0xd9, 0x41, 0x73, 0xa9, 0xab, 0x45, 0xb8, 0x71, 0x60, 0x58, + 0x18, 0xd4, 0x16, 0x2c, 0x65, 0xba, 0xd1, 0x05, 0xde, 0x92, 0xc5, 0x50, + 0x10, 0x11, 0x90, 0xce, 0x47, 0xcc, 0xfb, 0xaf, 0xbf, 0x23, 0xc0, 0x9f, + 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x67, 0x30, 0x65, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, + 0x01, 0x86, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0b, 0xb3, 0x68, 0x3b, 0xaf, 0xda, + 0xaf, 0xee, 0x70, 0xa5, 0x76, 0xd9, 0x21, 0xf7, 0xcc, 0x44, 0x16, 0x07, + 0xd0, 0xf8, 0x30, 0x10, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, + 0x37, 0x15, 0x01, 0x04, 0x03, 0x02, 0x01, 0x00, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30, 0x08, 0x30, 0x06, 0x06, 0x04, 0x55, + 0x1d, 0x20, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0c, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x48, + 0xc7, 0x37, 0xff, 0xff, 0xc1, 0x68, 0x57, 0xd7, 0x8b, 0x43, 0x66, 0x46, + 0x3a, 0x26, 0x6b, 0x2f, 0xe8, 0xfa, 0xde, 0x68, 0xa1, 0x8f, 0x47, 0xf1, + 0x3d, 0x34, 0x95, 0x7a, 0xda, 0x55, 0x31, 0xf4, 0x95, 0xd2, 0x38, 0x5f, + 0x2c, 0xba, 0x8f, 0xa5, 0x8d, 0x51, 0x31, 0x6a, 0x89, 0x55, 0x68, 0x6c, + 0x2b, 0x42, 0x64, 0x6a, 0x85, 0x24, 0xa0, 0x51, 0x03, 0xc7, 0xdd, 0xd1, + 0x72, 0x58, 0xed, 0x6c, 0x1e, 0x8c, 0xd8, 0x91, 0xc5, 0xe7, 0x49, 0x11, + 0x9d, 0x19, 0x7a, 0x37, 0x58, 0x1e, 0x77, 0x44, 0xfb, 0xc2, 0x08, 0x98, + 0x42, 0xc4, 0x4d, 0xe3, 0x9b, 0x8a, 0x0e, 0xcf, 0x40, 0x45, 0x4f, 0x1b, + 0x80, 0x70, 0x59, 0x8c, 0x93, 0x81, 0xe8, 0x0f, 0xd5, 0xc8, 0x26, 0x95, + 0xa9, 0xf7, 0x1f, 0x77, 0x06, 0xb8, 0xca, 0xef, 0x9c, 0xfb, 0xe8, 0x66, + 0xda, 0xe5, 0x39, 0xe0, 0xd2, 0xd2, 0x62, 0xc3, 0xa7, 0xd4, 0xb6, 0x18, + 0x9a, 0x27, 0x9b, 0x26, 0x50, 0x4a, 0x72, 0x97, 0xd5, 0xb3, 0x5b, 0x2a, + 0xa4, 0xfd, 0x5f, 0x2f, 0x7e, 0xe6, 0x62, 0xa3, 0x27, 0x66, 0x0c, 0xfa, + 0xd9, 0x19, 0xcc, 0x11, 0x1d, 0x31, 0xa8, 0x01, 0x52, 0x08, 0xe6, 0x54, + 0x0c, 0x99, 0x63, 0x2b, 0xea, 0xd8, 0x84, 0xd4, 0xb4, 0x08, 0x16, 0xef, + 0xbe, 0x4a, 0x5b, 0x88, 0x58, 0xf4, 0x06, 0x16, 0xa0, 0xeb, 0x7a, 0x5d, + 0xe1, 0xc7, 0x44, 0xd6, 0xbb, 0x2f, 0x55, 0x56, 0x25, 0xf0, 0x9e, 0x0c, + 0xe4, 0x0f, 0x12, 0xdb, 0xc0, 0x7f, 0xaf, 0x56, 0x5d, 0xc6, 0x89, 0x0e, + 0x71, 0xa9, 0x56, 0x12, 0xe4, 0xb9, 0x9c, 0xa8, 0x64, 0x1e, 0xb5, 0x47, + 0x95, 0x92, 0xae, 0xd0, 0x70, 0xc8, 0x93, 0x7d, 0x7c, 0x5a, 0x58, 0xf1, + 0x05, 0xf1, 0x4a, 0xb8, 0x6c, 0x72, 0x18, 0xa9, 0xae, 0x1f, 0x57, 0x99, + 0x26, 0x74, 0x66, 0xf5, 0x1d, 0x0f, 0xdf, 0x5d, 0xf0, 0xe7, 0x37, 0x5b, + 0x5f, 0xba, 0xf0, 0xb4, 0xef, 0xe4, 0x63, 0x07, 0x7e, 0x1f, 0x32, 0x18, + 0x69, 0xa9, 0x70, 0x5a, 0x92, 0xf9, 0x79, 0x9c, 0x58, 0xd4, 0x7e, 0xbf, + 0x72, 0x5d, 0x53, 0x46, 0x2b, 0x6e, 0xa3, 0x99, 0x60, 0xd6, 0x85, 0x8c, + 0x66, 0x77, 0x16, 0x76, 0xaf, 0xe2, 0xc5, 0x18, 0x5b, 0xe2, 0x5d, 0x08, + 0x36, 0xd6, 0x66, 0x37, 0x17, 0x65, 0xf0, 0x2e, 0xcf, 0xa1, 0xe5, 0xbc, + 0xe6, 0x8d, 0x0d, 0x65, 0xb4, 0x56, 0x53, 0x5d, 0x9f, 0xc8, 0xaf, 0x4e, + 0x6e, 0x51, 0xcf, 0x88, 0xbe, 0x92, 0xea, 0x30, 0xfb, 0x2c, 0xe7, 0x75, + 0x3f, 0x42, 0x60, 0xc4, 0x71, 0xe7, 0x97, 0x9f, 0x73, 0xc7, 0x9f, 0xca, + 0xd1, 0xb8, 0x6c, 0x23, 0xea, 0x50, 0x28, 0x1d, 0x0e, 0x43, 0xcc, 0xf5, + 0xa9, 0x1b, 0x40, 0xeb, 0xa6, 0x98, 0xe5, 0xe5, 0x0f, 0xc5, 0x92, 0x2f, + 0xa5, 0x96, 0xc7, 0xd7, 0xfa, 0x3c, 0x18, 0xee, 0x1d, 0x1b, 0x61, 0x03, + 0xfd, 0x86, 0xe7, 0x24, 0x41, 0x33, 0xbd, 0xd8, 0xf3, 0xb6, 0x60, 0x7c, + 0xf3, 0x1c, 0x82, 0x03, 0xd5, 0x60, 0xaf, 0xdf, 0xf4, 0x20, 0xa4, 0xe4, + 0x81, 0x06, 0x22, 0x5a, 0xcc, 0x85, 0x33, 0x7d, 0x64, 0xf8, 0xe4, 0xb8, + 0xbf, 0x80, 0x17, 0xd4, 0xfb, 0x21, 0x3f, 0x63, 0xae, 0xe7, 0x8f, 0xb7, + 0x17, 0x44, 0xec, 0x72, 0x2e, 0x35, 0xc9, 0x0b, 0xd0, 0x81, 0x1d, 0xe9, + 0x72, 0x03, 0x09, 0x41, 0xd9, 0xdf, 0x09, 0x48, 0xe6, 0xcd, 0xb7, 0xb2, + 0x1c, 0x60, 0x25, 0x19, 0x52, 0xf3, 0x3d, 0x12, 0x49, 0xed, 0x9d, 0x94, + 0x22, 0x8e, 0x71, 0x28, 0xf8, 0xc1, 0x07, 0x54, 0x73, 0xdd, 0x38, 0x08, + 0xb4, 0x85, 0x8f, 0x14, 0x6c, 0xaa, 0x00, 0xaf, 0x40, 0xab, 0xb5, 0x87, + 0xce, 0xb6, 0x39, 0x5c, 0x73, 0xf9, 0x90, 0x18, 0x22, 0x82, 0x2f, 0x58, + 0x20, 0x42, 0x81, 0x07, 0x43, 0xe2, 0x72, 0x4c, 0x0e, 0x4b, 0xae, 0x91, + 0x2c, 0x8b, 0x65, 0xc6, 0xd5, 0x23, 0x36, 0xd7, 0x44, 0x76, 0x1d, 0xb1, + 0x85, 0x03, 0xc3, 0x54, 0xe6, 0xf3, 0xad, 0xe4, 0x57, 0x19, 0x01, 0x02, + 0x38, 0x2a, 0x19, 0x01, 0x03, 0x78, 0x1c, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x69, + 0x6e, 0x2d, 0x74, 0x6f, 0x74, 0x6f, 0x2b, 0x6a, 0x73, 0x6f, 0x6e, 0xa0, + 0x58, 0x30, 0x93, 0x52, 0xac, 0x02, 0x8d, 0x83, 0x03, 0xb1, 0xf1, 0xa3, + 0x57, 0xdb, 0xda, 0x7b, 0x8b, 0x0f, 0x1a, 0xd8, 0x8f, 0xa8, 0xbb, 0x5b, + 0x31, 0xe6, 0xa4, 0x02, 0xcc, 0x27, 0x82, 0x79, 0x48, 0x94, 0x04, 0x87, + 0x9a, 0xc2, 0x0e, 0x12, 0xaf, 0xb2, 0x30, 0x0e, 0xd6, 0xb8, 0xd1, 0xb9, + 0x24, 0x30, 0x59, 0x01, 0x80, 0x4a, 0x62, 0x13, 0x03, 0xff, 0x92, 0xb3, + 0x2f, 0x03, 0xe3, 0x66, 0x88, 0xaa, 0x1b, 0x55, 0xe8, 0x2a, 0x42, 0x08, + 0x2d, 0x75, 0xe4, 0xcc, 0x8f, 0x22, 0xa9, 0xea, 0x32, 0x80, 0x73, 0x89, + 0xce, 0x0c, 0x3d, 0xc5, 0xe8, 0xad, 0x0b, 0xe2, 0x12, 0x9a, 0xee, 0x02, + 0x37, 0xa4, 0x5d, 0xfc, 0x63, 0x57, 0x6b, 0x38, 0x0e, 0xbb, 0xd7, 0x22, + 0x14, 0x00, 0x86, 0x1d, 0x59, 0x41, 0xa5, 0xe5, 0x41, 0xde, 0x7e, 0xb8, + 0x6d, 0x92, 0x62, 0x42, 0x7a, 0xc3, 0x0d, 0xe4, 0xcc, 0x20, 0x65, 0xcb, + 0x65, 0xa8, 0x76, 0x28, 0x62, 0xe2, 0xf6, 0xce, 0x48, 0x0e, 0x22, 0x9b, + 0x3f, 0xc7, 0x02, 0xcd, 0x3c, 0x31, 0xba, 0x09, 0xe3, 0xdb, 0xe9, 0x21, + 0xc9, 0x7f, 0x33, 0xb1, 0xa0, 0x25, 0x73, 0x78, 0x31, 0xd8, 0x00, 0x8d, + 0x7a, 0x67, 0x1c, 0x7a, 0x03, 0xf8, 0x26, 0x4d, 0xbe, 0x03, 0x8e, 0x1d, + 0x01, 0x80, 0x8e, 0x1e, 0x5a, 0x54, 0x53, 0x17, 0xdb, 0x5c, 0xc8, 0x60, + 0xb3, 0x3a, 0xe7, 0x85, 0xa0, 0xa4, 0x16, 0xb6, 0x6c, 0xfd, 0x75, 0xcc, + 0x15, 0x25, 0x38, 0xa6, 0x70, 0x62, 0xd0, 0x70, 0x00, 0xfe, 0x4e, 0x74, + 0x70, 0xc6, 0x6a, 0x52, 0xe0, 0x0d, 0x5c, 0x28, 0x3f, 0x82, 0x0e, 0x2b, + 0x53, 0x61, 0x26, 0x2c, 0x2f, 0x93, 0xbb, 0x9c, 0x22, 0x63, 0x69, 0xc8, + 0x6d, 0xd2, 0x79, 0xe7, 0x4b, 0x63, 0x97, 0xe6, 0x59, 0x7b, 0x71, 0x6d, + 0x21, 0xa8, 0xa9, 0x4d, 0x25, 0x84, 0x70, 0x3d, 0x03, 0x1e, 0x54, 0xac, + 0x8e, 0xdb, 0x96, 0xa1, 0x34, 0x4b, 0x80, 0xda, 0xa3, 0x11, 0x13, 0x69, + 0x98, 0x23, 0x34, 0xbe, 0x93, 0x89, 0x50, 0xa8, 0x79, 0x39, 0x5f, 0xf2, + 0x50, 0x21, 0xa7, 0x9a, 0x01, 0x8e, 0x43, 0x31, 0xe0, 0x26, 0x09, 0xe2, + 0x07, 0x97, 0x3c, 0xc7, 0x31, 0x04, 0x2b, 0x2c, 0x60, 0xa3, 0xed, 0x91, + 0xfc, 0xd7, 0xd0, 0x30, 0xa6, 0x56, 0xf6, 0x67, 0xb3, 0x6d, 0x7c, 0xa5, + 0x86, 0xd0, 0x09, 0x91, 0x1b, 0xfc, 0xf9, 0xcb, 0x15, 0x5a, 0x13, 0x6e, + 0xd3, 0x6b, 0x53, 0xaf, 0x91, 0x0f, 0xaa, 0xf4, 0x0b, 0xb1, 0x56, 0x0f, + 0xbc, 0x76, 0x5e, 0xe3, 0xe2, 0xd8, 0x19, 0xd7, 0xe5, 0x4d, 0xcd, 0xbb, + 0x0a, 0x02, 0x0a, 0x25, 0x3e, 0xf4, 0x6c, 0xd6, 0xc9, 0x6c, 0x31, 0x4f, + 0x5a, 0xd4, 0xb6, 0xa2, 0x3e, 0x15, 0x10, 0x93, 0x00, 0xb1, 0xa7, 0x32, + 0xfc, 0x1d, 0x79, 0x5a, 0x16, 0xf6, 0x8d, 0x87, 0xce, 0x06, 0xa0, 0xf4, + 0xe9, 0x18, 0x8c, 0xec, 0xef, 0xe0, 0x10, 0x1c, 0xaa, 0x1f, 0xe7, 0xa1, + 0x85, 0x16, 0x32, 0xf3, 0xae, 0x4b, 0x3c, 0xf9, 0xf4, 0xdb, 0x38, 0xd9, + 0xb3, 0xef, 0xca, 0x8d, 0x76, 0x6c, 0x94, 0x62, 0x45, 0x9f, 0x35, 0x5f, + 0xf9, 0xb5, 0xae, 0x50, 0x4f, +} diff --git a/app.go b/app.go index db5599c..1b25d6a 100644 --- a/app.go +++ b/app.go @@ -1,3 +1,4 @@ +// Package veracity provides the main application for the Veracity CLI tool. package veracity import ( @@ -7,7 +8,6 @@ import ( ) func NewApp(version string, ikwid bool) *cli.App { - cli.VersionPrinter = func(cCtx *cli.Context) { fmt.Println(cCtx.App.Version) } @@ -69,6 +69,7 @@ func AddCommands(app *cli.App, ikwid bool) *cli.App { app.Commands = append(app.Commands, NewNodeScanCmd()) app.Commands = append(app.Commands, NewFindTrieEntriesCmd()) app.Commands = append(app.Commands, NewFindMMREntriesCmd()) + app.Commands = append(app.Commands, NewAppendCmd()) } return app } diff --git a/append.go b/append.go new file mode 100644 index 0000000..80cdf1f --- /dev/null +++ b/append.go @@ -0,0 +1,545 @@ +package veracity + +import ( + "bytes" + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "time" + + "github.com/fxamacker/cbor/v2" + "github.com/veraison/go-cose" + + commoncose "github.com/datatrails/go-datatrails-common/cose" + "github.com/datatrails/go-datatrails-merklelog/massifs" + "github.com/datatrails/go-datatrails-merklelog/mmr" + "github.com/datatrails/veracity/keyio" + "github.com/datatrails/veracity/localmassifs" + "github.com/datatrails/veracity/scitt" + "github.com/urfave/cli/v2" +) + +// coseSigner implements IdentifiableCoseSigner +type identifiableCoseSigner struct { + innerSigner cose.Signer + publicKey ecdsa.PublicKey +} + +func (s *identifiableCoseSigner) Algorithm() cose.Algorithm { + return s.innerSigner.Algorithm() +} + +func (s *identifiableCoseSigner) Sign(rand io.Reader, content []byte) ([]byte, error) { + return s.innerSigner.Sign(rand, content) +} + +func (s *identifiableCoseSigner) LatestPublicKey() (*ecdsa.PublicKey, error) { + return &s.publicKey, nil +} + +func (s *identifiableCoseSigner) PublicKey(ctx context.Context, kid string) (*ecdsa.PublicKey, error) { + return &s.publicKey, nil +} + +func (s *identifiableCoseSigner) KeyLocation() string { + return "robinbryce.me" +} + +func (s *identifiableCoseSigner) KeyIdentifier() string { + // the returned kid needs to match the kid format of the keyvault key + return "location:robinbryce/version1" +} + +// NewAppendCmd appends an entry to a local ledger, optionally sealing it with a provided private key. +func NewAppendCmd() *cli.Command { + return &cli.Command{ + Name: "append", + Usage: "add an entry to a local ledger, optionally sealing it with a provided private key", + Flags: []cli.Flag{ + &cli.Uint64Flag{ + Name: "mmrindex", Aliases: []string{"i"}, + }, + &cli.Int64Flag{ + Name: "massif", Aliases: []string{"m"}, + Usage: "allow inspection of an arbitrary mmr index by explicitly specifying a massif index", + Value: -1, + }, + &cli.StringFlag{ + Name: "sealer-key", + Usage: "the sealer key to use for signing the entry, in cose .cbor. Only P-256, ES256 is supported. If --generate-sealer-key is set, this generated key will be written to this file.", + }, + &cli.StringFlag{ + Name: "sealer-key-pem", + Usage: "the sealer key to use for signing the entry, in PEM format. Only P-256, ES256 is supported. If --generate-sealer-key is set, this generated key will be written to this file.", + }, + &cli.StringFlag{ + Name: "sealer-public-key-pem", + Usage: "If set, and if the sealer key is generated, the public key in PEM format is saved to this file.", + }, + + &cli.StringFlag{ + Name: "trusted-sealer-key-pem", + Usage: "verify the current seal using this pem file based public key", + }, + + &cli.StringFlag{ + Name: "receipt-file", + Usage: "file name to write the receipt to, defaults to 'receipt-{mmrIndex}.cbor'", + }, + + &cli.StringFlag{ + Name: "signed-statement", + Usage: "read statement to register from this file. if statements-dir is also set, this statement is registered first, then all statements in the directory are registered.", + }, + &cli.StringFlag{ + Name: "statements-dir", + Usage: "read statements to register from this directory. the statements are added in lexical filename order", + }, + + &cli.BoolFlag{ + Name: "generate-sealer-key", + Usage: "generate an ephemeral sealer key and write it to the sealer-key file. If the sealer-key file already exists, it will be overwritten. the default file name is 'ecdsa-key-private.cbor'.", + }, + &cli.StringFlag{ + Name: "massifs-dir", + Usage: "the directory to read the massifs from.", + }, + &cli.StringFlag{ + Name: "seals-dir", + Usage: "the directory to read the massif seals from.", + }, + }, + Action: func(cCtx *cli.Context) error { + var err error + cmd := &CmdCtx{} + + if !cCtx.IsSet("data-local") { + return errors.New("this command supports local replicas only, and requires --data-local") + } + err = cfgLogging(cmd, cCtx) + if err != nil { + return fmt.Errorf("failed to configure logging: %w", err) + } + err = cfgIDState(cmd, cCtx) + if err != nil { + return fmt.Errorf("failed to configure id state: %w", err) + } + if cmd.cborCodec, err = massifs.NewRootSignerCodec(); err != nil { + return err + } + + if err = cfgMassifReader(cmd, cCtx); err != nil { + return err + } + + // + // Read or generate a key to seal the forked log + // + var sealingKey *ecdsa.PrivateKey + if cCtx.IsSet("sealer-key") && !cCtx.Bool("generate-sealer-key") { + sealerKeyFile := cCtx.String("sealer-key") + if sealerKeyFile == "" { + return errors.New("sealer-key file is required") + } + sealingKey, err = keyio.ReadECDSAPrivateCose(sealerKeyFile, "P-256") + if err != nil { + return fmt.Errorf("failed to load sealer key from file %s: %w", sealerKeyFile, err) + } + } + if cCtx.IsSet("sealer-key-pem") && !cCtx.Bool("generate-sealer-key") { + if cCtx.IsSet("sealer-key") { + fmt.Printf("verifying with sealer-key-pem %s (in preference to sealer-key)", cCtx.String("sealer-key-pem")) + } + sealerKeyFile := cCtx.String("sealer-key-pem") + if sealerKeyFile == "" { + return errors.New("sealer-key file is required") + } + sealingKey, err = keyio.ReadECDSAPrivatePEM(sealerKeyFile) + if err != nil { + return fmt.Errorf("failed to load sealer key from file %s: %w", sealerKeyFile, err) + } + } + + if cCtx.Bool("generate-sealer-key") { + sealingKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return err + } + } + + // + // Read the head of a locally replicated production datatrails ledger + // + readerCfg, err := localmassifs.NewReaderDefaultConfig( + cmd.log, + cCtx.String("massifs-dir"), + cCtx.String("seals-dir"), + ) + if err != nil { + return fmt.Errorf("failed to create massif reader config: %w", err) + } + verified, err := localmassifs.ReadVerifiedHeadMassif(readerCfg) + if err != nil { + return fmt.Errorf("failed to read verified head massif: %w", err) + } + + mmrSizeOrig := verified.RangeCount() + fmt.Printf("%8d verified-size\n", mmrSizeOrig) + verified.Tags = map[string]string{} + + // + // Add a batch of statements, including the in-toto from ammoury + // + statements, err := addStatements(cmd, cCtx, &verified.MassifContext) + if err != nil { + return err + } + fmt.Printf("%d statements registered\n", len(statements)) + mmrSizeNew := verified.RangeCount() + peakHashesNew, err := mmr.PeakHashes(&verified.MassifContext, mmrSizeNew-1) + if err != nil { + return err + } + for i, peak := range peakHashesNew { + fmt.Printf("peak[%d]: %x\n", i, peak) + } + + alg, err := commoncose.CoseAlgForEC(sealingKey.PublicKey) + if err != nil { + return err + } + + coseSigner, err := cose.NewSigner(alg, sealingKey) + if err != nil { + return err + } + identifiableSigner := &identifiableCoseSigner{ + innerSigner: coseSigner, + publicKey: sealingKey.PublicKey, + } + + // + // Seal a checkpoint for the locally forked ledger with a made up sealing key + // Receipts are rooted at a checkpoint accumulator state. + // + rootSigner := massifs.NewRootSigner("https://github.com/robinbryce/veracity", cmd.cborCodec) + + // TODO: account for filling a massif + mmrSizeCurrent := verified.RangeCount() + cp, err := mmr.IndexConsistencyProof(&verified.MassifContext, verified.MMRState.MMRSize-1, mmrSizeCurrent-1) + if err != nil { + return err + } + + // To create the checkpoint, we first check that the current state + // contains the previously verified state. This necessarily produces + // and verifies the new accumulator which we can then include with + // the new checkpoint. + ok, peaksB, err := mmr.CheckConsistency( + verified, sha256.New(), + cp.MMRSizeA, cp.MMRSizeB, verified.MMRState.Peaks) + if !ok { + return fmt.Errorf("consistency check failed: verify failed") + } + if err != nil { + return err + } + lastIDTimestamp := verified.GetLastIdTimestamp() + + state := massifs.MMRState{ + Version: int(massifs.MMRStateVersionCurrent), + MMRSize: mmrSizeCurrent, + Peaks: peaksB, + Timestamp: time.Now().UnixMilli(), + CommitmentEpoch: verified.MMRState.CommitmentEpoch, + IDTimestamp: lastIDTimestamp, + } + + // + // Read and decode the checkpoint + // + // Given a signed checkpoint, receipts can be self served for any + // element included in the MMR before that checkpoint. Leaves from + // the massif corresponding to the massif need no other data. + // Leaves from earlier massifs *may* need the earlier massif, but + // often don't (its deterministic and computable when and which + // earlier massifs are needed for an arbitrary mmrIndex) + // + // It is never necessary to have more than two massifs in order to + // produce a receipt against the latest checkpoint. + // + // There is no particular reason to re-fresh, or even save, receipts + // if you have a trustworthy store of checkpoints. + // + mmrStatement := statements[0] + // A more appropriate subject would be the identity of the log ... + subject := fmt.Sprintf("fork-%d-%d.bin", verified.MMRState.MMRSize-1, mmrSizeCurrent) + publicKey, err := identifiableSigner.LatestPublicKey() + if err != nil { + return fmt.Errorf("unable to get public key for signing key %w", err) + } + + keyIdentifier := identifiableSigner.KeyIdentifier() + data, err := rootSigner.Sign1(coseSigner, keyIdentifier, publicKey, subject, state, nil) + if err != nil { + return err + } + + // note that state is not verified here, but we just signed it so it is our droid + msg, state, err := massifs.DecodeSignedRoot(cmd.cborCodec, data) + if err != nil { + return err + } + + // + // Generate the inclusion proof, note that we don't actually need + // the leaf hash to do this. So *anyone* can obtain a receipt for + // *any* leaf at any time, given only the specific massif (tile) + // that leaf was registered in. and its associated checkpoint. + // + proof, err := mmr.InclusionProof(&verified.MassifContext, state.MMRSize-1, mmrStatement.MMRIndexLeaf) + if err != nil { + return fmt.Errorf( + "failed to generating inclusion proof: %d in MMR(%d), %v", + mmrStatement.MMRIndexLeaf, verified.MMRState.MMRSize, err) + } + + // + // Locate the pre-signed receipt for the accumulator peak containing the leaf. + // + peakIndex := mmr.PeakIndex(mmr.LeafCount(state.MMRSize), len(proof)) + // NOTE: The old-accumulator compatibility property, from + // https://eprint.iacr.org/2015/718.pdf, along with the COSE protected & + // unprotected buckets, is why we can just pre sign the receipts. + // As long as the receipt consumer is convinced of the logs consistency (not split view), + // it does not matter which accumulator state the receipt is signed against. + + var peaksHeader massifs.MMRStateReceipts + err = cbor.Unmarshal(msg.Headers.RawUnprotected, &peaksHeader) + if err != nil { + return fmt.Errorf( + "%w: failed decoding peaks header", err) + } + if peakIndex >= len(peaksHeader.PeakReceipts) { + return fmt.Errorf( + "%w: peaks header contains to few peak receipts", err) + } + + // This is an array of marshaled COSE_Sign1's + receiptMsg := peaksHeader.PeakReceipts[peakIndex] + signed, err := commoncose.NewCoseSign1MessageFromCBOR( + receiptMsg, commoncose.WithDecOptions(massifs.CheckpointDecOptions())) + if err != nil { + return fmt.Errorf( + "%w: failed to decode pre-signed receipt for MMR(%d)", + err, state.MMRSize) + } + + // To avoid creating invalid receipts due to bugs in this demo code, check the root matches the appropriate peak. + root := mmr.IncludedRoot(sha256.New(), mmrStatement.MMRIndexLeaf, mmrStatement.LeafHash, proof) + + if !bytes.Equal(root, peaksB[peakIndex]) { + return fmt.Errorf( + "%w: root %x of leaf %d in MMR(%d) does not match peak %d %x", + ErrVerifyInclusionFailed, root, mmrStatement.MMRIndexLeaf, state.MMRSize, peakIndex, state.Peaks[peakIndex]) + } + + // + // Make the MMR draft receipt by attaching the inclusion proof to the Unprotected header + // + signed.Headers.RawUnprotected = nil + + verifiableProofs := massifs.MMRiverVerifiableProofs{ + InclusionProofs: []massifs.MMRiverInclusionProof{{ + Index: mmrStatement.MMRIndexLeaf, + InclusionPath: proof, + }}, + } + + tagOriginSubject := int64(-257) + tagOriginIssuer := tagOriginSubject - 1 + tagLeafHash := tagOriginSubject - 2 + tagIDTimestamp := tagOriginSubject - 3 + tagExtraBytes := tagOriginSubject - 4 + + signed.Headers.Unprotected[massifs.VDSCoseReceiptProofsTag] = verifiableProofs + // these values would usually be provided by the application, or obtained directly from any replica. + // the unprotected headers are not signed, and are intended for this sort of convenience. + signed.Headers.Unprotected[tagOriginIssuer] = mmrStatement.Claims.Issuer + signed.Headers.Unprotected[tagOriginSubject] = mmrStatement.Claims.Subject + signed.Headers.Unprotected[tagIDTimestamp] = mmrStatement.IDTimestamp + signed.Headers.Unprotected[tagExtraBytes] = mmrStatement.ExtraBytes + signed.Headers.Unprotected[tagLeafHash] = mmrStatement.LeafHash + // + // Save the receipt to a file + // + receiptCbor, err := signed.MarshalCBOR() + if err != nil { + return fmt.Errorf("failed to marshal receipt: %w", err) + } + + receiptFileName := cCtx.String("receipt-file") + if receiptFileName == "" { + receiptFileName = fmt.Sprintf("receipt-%d.cbor", mmrStatement.MMRIndexLeaf) + } + if err := os.WriteFile(receiptFileName, receiptCbor, os.FileMode(0644)); err != nil { + return fmt.Errorf("failed to write receipt file %s: %w", receiptFileName, err) + } + fmt.Printf("wrote receipt file %s\n", receiptFileName) + + // + // A bunch of persistence conveniences for the sake of the demo + // + + forkFileName := filepath.Join(".", fmt.Sprintf("fork-%d-%d.bin", verified.MMRState.MMRSize-1, mmrSizeCurrent)) + if err := os.WriteFile(forkFileName, data, os.FileMode(0644)); err != nil { + return fmt.Errorf("failed to write log fork file %s: %w", forkFileName, err) + } + fmt.Printf("wrote forked log massif file %s\n", forkFileName) + + checkpointFileName := filepath.Join(".", fmt.Sprintf("checkpoint-%d.cbor", mmrSizeCurrent)) + if err := os.WriteFile(checkpointFileName, data, os.FileMode(0644)); err != nil { + return fmt.Errorf("failed to write checkpoint file %s: %w", checkpointFileName, err) + } + fmt.Printf("wrote checkpoint file %s\n", checkpointFileName) + if cCtx.Bool("generate-sealer-key") { + // write the sealer key to the sealer-key file + sealerKeyFile := cCtx.String("sealer-key") + if sealerKeyFile == "" { + sealerKeyFile = keyio.ECDSAPrivateDefaultFileName + } + if _, err := keyio.WriteECDSAPrivateCOSE(sealerKeyFile, sealingKey); err != nil { + return fmt.Errorf("failed to write sealer key to file %s: %w", sealerKeyFile, err) + } + fmt.Printf("wrote sealer key to file %s\n", sealerKeyFile) + sealerKeyFile = cCtx.String("sealer-key-pem") + if sealerKeyFile == "" { + sealerKeyFile = keyio.ECDSAPrivateDefaultPEMFileName + } + if err := keyio.WriteECDSAPrivatePEM(sealerKeyFile, sealingKey); err != nil { + return fmt.Errorf("failed to write sealer key to file %s: %w", sealerKeyFile, err) + } + fmt.Printf("wrote sealer key to file %s\n", sealerKeyFile) + + sealerKeyFile = cCtx.String("sealer-public-key-pem") + if sealerKeyFile == "" { + sealerKeyFile = keyio.ECDSAPublicDefaultPEMFileName + } + if _, err := keyio.WriteCoseECDSAPublicKey(sealerKeyFile, &sealingKey.PublicKey); err != nil { + return fmt.Errorf("failed to write sealer key to file %s: %w", sealerKeyFile, err) + } + fmt.Printf("wrote sealer public key to file %s\n", sealerKeyFile) + } + return nil + }, + } +} + +// addStatements adds the signed statements to the massif and returns the leaf +// indices of the added statements. +// If a specific statement is specified via --signed-statement, then it is +// added first. THose discovered from --statement-dir are added in lexical +// filename order. +func addStatements(cmd *CmdCtx, cCtx *cli.Context, massif *massifs.MassifContext) ([]scitt.MMRStatement, error) { + var fileNames []string + var statements []scitt.MMRStatement + + if cCtx.String("signed-statement") != "" { + fileNames = append(fileNames, cCtx.String("signed-statement")) + } + + if cCtx.String("statements-dir") != "" { + files, err := listFilesWithSuffix(cCtx.String("statements-dir"), ".cbor") + if err != nil { + return nil, err + } + fileNames = append(fileNames, files...) + } + if len(fileNames) == 0 { + return nil, fmt.Errorf("no signed statements found, please specify --signed-statement or --statements-dir or both") + } + + for _, fileName := range fileNames { + mmrStatement, err := readStatementFromFile(fileName, cmd) + if err != nil { + return nil, fmt.Errorf("failed to read signed statement from file %s: %w", cCtx.String("signed-statement"), err) + } + + // the *next* index to be added is the current *count* + mmrStatement.MMRIndexLeaf = massif.RangeCount() + + _, err = massif.AddHashedLeaf( + sha256.New(), + mmrStatement.IDTimestamp, + mmrStatement.ExtraBytes, + // use the issuer as the origin log id, which isn't quite right, but is close enough for this demo + []byte(mmrStatement.Claims.Issuer), + []byte("scitt"), + mmrStatement.LeafHash, + ) + if err != nil { + return nil, fmt.Errorf("failed to add hashed leaf: %w", err) + } + + statements = append(statements, *mmrStatement) + + fmt.Printf("index : %d\n", mmrStatement.MMRIndexLeaf) + fmt.Printf(" issuer : %s\n", mmrStatement.Claims.Issuer) + fmt.Printf(" idtimestamp : %x\n", mmrStatement.IDTimestamp) + fmt.Printf(" extraBytes : %x\n", mmrStatement.ExtraBytes) + fmt.Printf(" leaf-hash : %x\n", mmrStatement.LeafHash) + fmt.Printf(" statement-hash : %x\n", mmrStatement.Hash) + fmt.Printf(" node count : %d\n", (len(massif.Data)-int(massif.LogStart()))/32) + + value, err := massif.Get(mmrStatement.MMRIndexLeaf) + if err != nil { + return nil, fmt.Errorf("failed to get leaf value for index %d: %w", mmrStatement.MMRIndexLeaf, err) + } + if !bytes.Equal(value, mmrStatement.LeafHash) { + // this will mean a bug in the hacked up code if it catches + return nil, fmt.Errorf("leaf hash %x does not match expected value %x for index %d", + value, mmrStatement.LeafHash, mmrStatement.MMRIndexLeaf) + } + } + return statements, nil +} + +func readStatementFromFile(fileName string, cmd *CmdCtx) (*scitt.MMRStatement, error) { + mmrStatement, cpd, err := scitt.NewMMRStatementFromFile(fileName, cmd, scitt.RegistrationPolicyVerified()) + if err != nil { + if cpd != nil && cpd.Instance != scitt.ProblemInstanceConfirmationMissing { + return nil, fmt.Errorf("%w: failed reading and checking signed statement: %s", err, cpd.Detail) + } + // for demo purposes, because we do not support x509 + mmrStatement, cpd, err = scitt.NewMMRStatementFromFile(fileName, cmd, scitt.RegistrationPolicyUnverified()) + if err != nil { + return nil, fmt.Errorf("%w: failed reading and checking signed statement: %s", err, cpd.Detail) + } + err = nil + cpd = nil + } + return mmrStatement, nil +} + +func listFilesWithSuffix(dir, suffix string) ([]string, error) { + entries, err := os.ReadDir(dir) + if err != nil { + return nil, err + } + + var matches []string + for _, entry := range entries { + if entry.Type().IsRegular() && strings.HasSuffix(entry.Name(), suffix) { + matches = append(matches, filepath.Join(dir, entry.Name())) + } + } + return matches, nil +} diff --git a/cfgidstate.go b/cfgidstate.go new file mode 100644 index 0000000..5560cb8 --- /dev/null +++ b/cfgidstate.go @@ -0,0 +1,25 @@ +package veracity + +import ( + "github.com/datatrails/go-datatrails-merklelog/massifs/snowflakeid" + "github.com/urfave/cli/v2" +) + +// cfgIDState initialises an idTimestamp generator +// of enabled workarounds. +func cfgIDState(cmd *CmdCtx, cCtx *cli.Context) error { + var err error + cmd.commitmentEpoch = 1 // the default, and correct until the unix epoch changes + if cCtx.IsSet("commitment-epoch") { + cmd.commitmentEpoch = uint8(cCtx.Uint64("commitment-epoch")) + } + + cmd.idState, err = snowflakeid.NewIDState(snowflakeid.Config{ + CommitmentEpoch: cmd.commitmentEpoch, + // There is no reason to override these for local use. + WorkerCIDR: "0.0.0.0/16", + PodIP: "10.0.0.127", + }) + + return err +} diff --git a/cfgmassif.go b/cfgmassif.go index 07bb63b..043d31a 100644 --- a/cfgmassif.go +++ b/cfgmassif.go @@ -6,6 +6,7 @@ import ( "github.com/datatrails/go-datatrails-common/logger" "github.com/datatrails/go-datatrails-merklelog/massifs" + "github.com/datatrails/veracity/localmassifs" "github.com/urfave/cli/v2" ) @@ -57,10 +58,11 @@ func cfgMassifReader(cmd *CmdCtx, cCtx *cli.Context) error { // not automatically derived. cache, err := massifs.NewLogDirCache( logger.Sugar, - NewFileOpener(), - massifs.WithDirCacheTenant(cCtx.String("tenant")), // may be empty string - massifs.WithDirCacheMassifLister(NewDirLister()), - massifs.WithDirCacheSealLister(NewDirLister()), + localmassifs.NewFileOpener(), + massifs.WithDirCacheTenant(cCtx.String("tenant")), + // massifs.WithExplicitFilePaths(cCtx.String("tenant")), // may be empty string + massifs.WithDirCacheMassifLister(localmassifs.NewDirLister()), + massifs.WithDirCacheSealLister(localmassifs.NewDirLister()), massifs.WithReaderOption(massifs.WithMassifHeight(cmd.massifHeight)), massifs.WithReaderOption(massifs.WithCBORCodec(codec)), ) diff --git a/cfgreader.go b/cfgreader.go index f62bf27..53080d2 100644 --- a/cfgreader.go +++ b/cfgreader.go @@ -80,7 +80,7 @@ func cfgReader(cmd *CmdCtx, cCtx *cli.Context, forceProdUrl bool) (azblob.Reader } cmd.readerURL = url - reader, err = azblob.NewReaderNoAuth(url, azblob.WithContainer(container), azblob.WithAccountName(account)) + reader, err = azblob.NewReaderNoAuth(cmd.log, url, azblob.WithContainer(container), azblob.WithAccountName(account)) if err != nil { return nil, fmt.Errorf("failed to connect to blob store: %v", err) } diff --git a/cmd/veracity/main.go b/cmd/veracity/main.go index c628338..9c7ba8c 100644 --- a/cmd/veracity/main.go +++ b/cmd/veracity/main.go @@ -1,10 +1,12 @@ +// Package main provides the entry point for the Veracity CLI application. package main import ( "fmt" "log" "os" - "strings" + + // "strings" "github.com/datatrails/veracity" ) @@ -20,18 +22,17 @@ var ( ) func main() { - versionString := "unknown" if version != "" { // versionString = fmt.Sprintf("%s %s %s", version, commit, buildDate) versionString = fmt.Sprintf("%s %s", version, commit) } - ikwid := false - envikwid := os.Getenv("VERACITY_IKWID") - if envikwid == "1" || strings.ToLower(envikwid) == "true" { - ikwid = true - } + ikwid := true + // envikwid := os.Getenv("VERACITY_IKWID") + // if envikwid == "1" || strings.ToLower(envikwid) == "true" { + // ikwid = true + // } app := veracity.NewApp(versionString, ikwid) veracity.AddCommands(app, ikwid) if err := app.Run(os.Args); err != nil { diff --git a/cmdctx.go b/cmdctx.go index 0fd7b10..14358fd 100644 --- a/cmdctx.go +++ b/cmdctx.go @@ -2,10 +2,12 @@ package veracity import ( "context" + "fmt" "github.com/datatrails/go-datatrails-common/cbor" "github.com/datatrails/go-datatrails-common/logger" "github.com/datatrails/go-datatrails-merklelog/massifs" + "github.com/datatrails/go-datatrails-merklelog/massifs/snowflakeid" ) // MassifGetter gets a specific massif based on the massifIndex given for a tenant log @@ -37,7 +39,7 @@ type MassifReader interface { type CmdCtx struct { log logger.Logger // storer *azblob.Storer - //reader azblob.Reader + // reader azblob.Reader massifReader MassifReader readerURL string cborCodec cbor.CBORCodec @@ -46,9 +48,19 @@ type CmdCtx struct { massifHeight uint8 + commitmentEpoch uint8 + idState *snowflakeid.IDState + bugs map[string]bool } +func (cmd *CmdCtx) NextID() (uint64, error) { + if cmd.idState == nil { + return 0, fmt.Errorf("idState not initialized, cannot generate next ID") + } + return cmd.idState.NextID() +} + // Clone returns a copy of the CmdCtx with only those members that are safe to share copied. // Those are: // - log - the result of cfgLogging diff --git a/diag.go b/diag.go index abd60ad..e3bfd35 100644 --- a/diag.go +++ b/diag.go @@ -65,7 +65,7 @@ func NewDiagCmd() *cli.Command { return err } tenant := cCtx.String("tenant") - if tenant == "" { + if tenant == "" && !cCtx.IsSet("data-local") { fmt.Println("a tenant is required to get diagnostics that require reading a blob") return nil } diff --git a/go.mod b/go.mod index de9b167..318e910 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,14 @@ module github.com/datatrails/veracity -go 1.23.0 +go 1.24.0 require ( - github.com/datatrails/go-datatrails-common v0.26.0 - github.com/datatrails/go-datatrails-common-api-gen v0.6.6 + github.com/datatrails/go-datatrails-common v0.30.0 + github.com/datatrails/go-datatrails-common-api-gen v0.8.0 github.com/datatrails/go-datatrails-logverification v0.4.3 - github.com/datatrails/go-datatrails-merklelog/massifs v0.4.0 - github.com/datatrails/go-datatrails-merklelog/mmr v0.2.0 - github.com/datatrails/go-datatrails-merklelog/mmrtesting v0.2.0 + github.com/datatrails/go-datatrails-merklelog/massifs v0.6.0 + github.com/datatrails/go-datatrails-merklelog/mmr v0.4.0 + github.com/datatrails/go-datatrails-merklelog/mmrtesting v0.4.0 github.com/datatrails/go-datatrails-serialization/eventsv1 v0.0.3 github.com/datatrails/go-datatrails-simplehash v0.0.5 github.com/gosuri/uiprogress v0.0.1 @@ -17,15 +17,21 @@ require ( golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 ) -// replace ( -// github.com/datatrails/go-datatrails-merklelog/massifs => ../go-datatrails-merklelog/massifs -// ) +replace ( + github.com/datatrails/go-datatrails-common => ../go-datatrails-common + github.com/datatrails/go-datatrails-logverification => ../go-datatrails-logverification + github.com/datatrails/go-datatrails-merklelog/massifs => ../go-datatrails-merklelog/massifs + github.com/datatrails/go-datatrails-merklelog/mmr => ../go-datatrails-merklelog/mmr + github.com/datatrails/go-datatrails-merklelog/mmrtesting => ../go-datatrails-merklelog/mmrtesting + github.com/datatrails/go-datatrails-serialization/eventsv1 => ../go-datatrails-serialization/eventsv1 + github.com/datatrails/go-datatrails-simplehash => ../go-datatrails-simplehash +) require ( - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect - github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/gosuri/uilive v0.0.4 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -37,9 +43,7 @@ require ( github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.7.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1 - github.com/Azure/go-amqp v1.0.5 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.29 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect @@ -53,33 +57,28 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/google/uuid v1.6.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect github.com/ldclabs/cose/go v0.0.0-20221214142927-d22c1cfc2154 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect - github.com/opentracing-contrib/go-stdlib v1.0.0 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 // indirect - github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/stretchr/testify v1.10.0 - github.com/veraison/go-cose v1.1.0 // indirect + github.com/veraison/go-cose v1.1.0 github.com/x448/float16 v0.8.4 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/grpc v1.69.0-dev // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/grpc v1.71.1 // indirect google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 295dbf3..d400d89 100644 --- a/go.sum +++ b/go.sum @@ -2,16 +2,14 @@ github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0 github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= -github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.7.1 h1:o/Ws6bEqMeKZUfj1RRm3mQ51O8JGU5w+Qdg2AhHib6A= -github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.7.1/go.mod h1:6QAMYBAbQeeKX+REFJMZ1nFWu9XLw/PPcjYpuc9RDFs= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1 h1:QSdcrd/UFJv6Bp/CfoVf2SrENpFn9P6Yh8yb+xNhYMM= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1/go.mod h1:eZ4g6GUvXiGulfIbbhh1Xr4XwUYaYaWMqzGD/284wCA= -github.com/Azure/go-amqp v1.0.5 h1:po5+ljlcNSU8xtapHTe8gIc8yHxCzC03E8afH2g1ftU= -github.com/Azure/go-amqp v1.0.5/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= @@ -38,49 +36,43 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= -github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/datatrails/go-datatrails-common v0.26.0 h1:Ga8lWKnA57VDFStrrO9OR394jMvV7S9Ia92m/7tGehs= -github.com/datatrails/go-datatrails-common v0.26.0/go.mod h1:k/ub6fdHldXZ129gzxDZI0aifi/qyFyKlU2P5bAASBM= -github.com/datatrails/go-datatrails-common-api-gen v0.6.6 h1:Qbfnte1+ZQsi0XzbfmOuk/xziqbPyEC4nyl7SsQdGdg= -github.com/datatrails/go-datatrails-common-api-gen v0.6.6/go.mod h1:rTMGdMdu5M6mGpbXZy1D84cBTGE8JwsDH6BYh9LJlmA= -github.com/datatrails/go-datatrails-logverification v0.4.3 h1:E+VKGudFanjbdGFLjB6L+r/9hHEOVrutplB/uMS+hs0= -github.com/datatrails/go-datatrails-logverification v0.4.3/go.mod h1:pWS2YhTuQJH0F/OgufihhM49gbck2BeWDo5Fll4JYqk= -github.com/datatrails/go-datatrails-merklelog/massifs v0.4.0 h1:j0mPW+sJruxGD+L9x59zu4muCWcNQIHtGYFDw6ZWolw= -github.com/datatrails/go-datatrails-merklelog/massifs v0.4.0/go.mod h1:9PzDUZzIMSLWcf5iv0AbzYOz6IhIBlHMXPiU5S1mb00= -github.com/datatrails/go-datatrails-merklelog/mmr v0.2.0 h1:NUP0OUVixuyWf+Gmi/e3wS5JD4za7DU0gdWVAgqnI5c= -github.com/datatrails/go-datatrails-merklelog/mmr v0.2.0/go.mod h1:iLipg39Ce3U68NjXFxjxwxXR9U0T6Dm6pldJA47Lx8s= -github.com/datatrails/go-datatrails-merklelog/mmrtesting v0.2.0 h1:cv8JincUm3h/4hyVcuofPt5pAtOZ+KYLnsDdvQ3D6Lc= -github.com/datatrails/go-datatrails-merklelog/mmrtesting v0.2.0/go.mod h1:h8b1O0xAoMv2DsVQuo6vNyM4RLL2DlJCWJqgysX127w= -github.com/datatrails/go-datatrails-serialization/eventsv1 v0.0.3 h1:BLHfCXjzXUgr1knXE9XtZC+jNnf2orGEL+BTAWqSyp4= -github.com/datatrails/go-datatrails-serialization/eventsv1 v0.0.3/go.mod h1:9i6Tip2lIXwSZ3SxP7XEhU2eQ9zkpxhEBmPmlOGqv/8= -github.com/datatrails/go-datatrails-simplehash v0.0.5 h1:igu4QRYO87RQXrJlqSm3fgMA2Q0F4jglWqBlfvKrXKQ= -github.com/datatrails/go-datatrails-simplehash v0.0.5/go.mod h1:XuOwViwdL+dyz7fGYIjaByS1ElMFsrVI0goKX0bNimA= +github.com/datatrails/go-datatrails-common v0.30.0 h1:QA95OPWe/UiqjGVdbMTzlOohgVYzbIY9X5KzDT2bohc= +github.com/datatrails/go-datatrails-common v0.30.0/go.mod h1:kfRSYLC/AUhDd7XOK2Do+13HbBKh2NU6Mw83K7zwaxs= +github.com/datatrails/go-datatrails-common-api-gen v0.8.0 h1:vO+s0h1SZMQv89240Fxok/vsJU4Oo/jHO5rbOVnj/pA= +github.com/datatrails/go-datatrails-common-api-gen v0.8.0/go.mod h1:ekmas39HNTCa011DG54jaKTVFh9XpAYZqITE414bD9U= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= -github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= -github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -93,8 +85,8 @@ github.com/gosuri/uiprogress v0.0.1 h1:0kpv/XY/qTmFWl/SkaJykZXrBBzwwadmW8fRb7RJS github.com/gosuri/uiprogress v0.0.1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -107,24 +99,15 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= -github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 h1:uhcF5Jd7rP9DVEL10Siffyepr6SvlKbUsjH5JpNCRi8= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0/go.mod h1:+oCZ5GXXr7KPI/DNOQORPTq5AWHfALJj9c72b0+YsEY= -github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= -github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -149,6 +132,18 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBi github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/bencode v1.0.0 h1:zgop0Wu1nu4IexAZeCZ5qbsjU4O1vMrfCrVgUjbHVuA= github.com/zeebo/bencode v1.0.0/go.mod h1:Ct7CkrWIQuLWAy9M3atFHYq4kG9Ao/SsY5cdtCXmp9Y= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -160,8 +155,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -172,8 +167,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -188,8 +183,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -202,19 +197,19 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g= -google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= -google.golang.org/grpc v1.69.0-dev h1:apWegzBczine6VjRA1FpkZ9LVAvNINTqDPbiRDD4D/g= -google.golang.org/grpc v1.69.0-dev/go.mod h1:2RINgKHklVDGHlkF/BfDsmIw0xdarBnd0YM+g7Fc0Fk= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= +google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -223,5 +218,3 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= -nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= diff --git a/keyio/ecdsareadwrite.go b/keyio/ecdsareadwrite.go new file mode 100644 index 0000000..9f87d42 --- /dev/null +++ b/keyio/ecdsareadwrite.go @@ -0,0 +1,266 @@ +package keyio + +import ( + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "math/big" + "os" + + commoncose "github.com/datatrails/go-datatrails-common/cose" + "github.com/fxamacker/cbor/v2" +) + +const ( + ECDSAPublicDefaultPEMFileName = "ecdsa-key-public.pem" + ECDSAPrivateDefaultPEMFileName = "ecdsa-key-private.pem" + ECDSAPublicDefaultFileName = "ecdsa-key-public.cbor" + ECDSAPrivateDefaultFileName = "ecdsa-key-private.cbor" + ECDSAPrivateDefaultPerm = 0600 // Default permission for private key file + ECDSAPublicDefaultPerm = 0644 // Default permission for private key file +) + +func ReadECDSAPublicCose( + fileName string, + expectedStandardCurve ...string, +) (*ecdsa.PublicKey, error) { + // Read the public key from the default file + data, err := os.ReadFile(fileName) + if err != nil { + return nil, fmt.Errorf("failed to read public key file: %w", err) + } + + publicKey, err := decodeECDSAPublicKey(data) + if err != nil { + return nil, fmt.Errorf("failed to decode public key: %w", err) + } + + if len(expectedStandardCurve) > 0 && + publicKey.Params().Name != expectedStandardCurve[0] { + return nil, fmt.Errorf("expected ECDSA public key with curve %s, got %s", + expectedStandardCurve[0], publicKey.Curve.Params().Name) + } + + return publicKey, nil +} + +func ReadECDSAPrivateCose( + fileName string, + expectedStandardCurve ...string, +) (*ecdsa.PrivateKey, error) { + // Read the private key from the default file + data, err := os.ReadFile(fileName) + if err != nil { + return nil, fmt.Errorf("failed to read private key file: %w", err) + } + privateKey, err := decodeECDSAPrivateKey(data, expectedStandardCurve...) + if err != nil { + return nil, fmt.Errorf("failed to decode private key: %w", err) + } + if len(expectedStandardCurve) > 0 && + privateKey.PublicKey.Params().Name != expectedStandardCurve[0] { + return nil, fmt.Errorf("expected ECDSA private key with curve %s, got %s", + expectedStandardCurve[0], privateKey.PublicKey.Curve.Params().Name) + } + return privateKey, nil +} + +func ReadECDSAPrivatePEM(filePath string) (*ecdsa.PrivateKey, error) { + pemData, err := os.ReadFile(filePath) + if err != nil { + return nil, err + } + + block, _ := pem.Decode(pemData) + if block == nil || block.Type != "EC PRIVATE KEY" { + return nil, errors.New("invalid PEM block or type") + } + + key, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + return nil, err + } + + return key, nil +} + +// Serializes the key to PEM format +func encodeECDSAPrivateKeyToPEM(key *ecdsa.PrivateKey) ([]byte, error) { + der, err := x509.MarshalECPrivateKey(key) + if err != nil { + return nil, err + } + block := &pem.Block{ + Type: "EC PRIVATE KEY", + Bytes: der, + } + return pem.EncodeToMemory(block), nil +} + +// Writes PEM to a file with 0600 permissions +func WriteECDSAPrivatePEM(pemFile string, key *ecdsa.PrivateKey) error { + pemBytes, err := encodeECDSAPrivateKeyToPEM(key) + if err != nil { + return fmt.Errorf("PEM encoding failed: %w", err) + } + return os.WriteFile(pemFile, pemBytes, 0600) +} + +func WriteECDSAPublicCOSE( + pubFile string, + publicKey *ecdsa.PublicKey, +) (string, error) { + var err error + + if _, err = WriteCoseECDSAPublicKey(pubFile, publicKey); err != nil { + return "", err + } + return pubFile, nil +} + +func WriteECDSAPrivateCOSE( + privFile string, + privateKey *ecdsa.PrivateKey, +) (string, error) { + var err error + + if _, err = WriteCoseECDSAPrivateKey(privFile, privateKey); err != nil { + return "", err + } + return privFile, nil +} + +// Encode private key to COSE_Key format (as CBOR bytes) +func encodePrivateKeyToCOSE(key *ecdsa.PrivateKey) ([]byte, error) { + m := map[int64]interface{}{ + commoncose.KeyTypeLabel: int64(commoncose.KeyTypeEC2), + commoncose.AlgorithmLabel: -7, // ES256 (ECDSA w/ SHA-256) + commoncose.ECCurveLabel: 1, // P-256 + commoncose.ECXLabel: key.PublicKey.X.Bytes(), + commoncose.ECYLabel: key.PublicKey.Y.Bytes(), + commoncose.ECDLabel: key.D.Bytes(), + } + return cbor.Marshal(m) +} + +// Encode public key to COSE_Key format (as CBOR bytes) +func encodePublicKeyToCOSE(key *ecdsa.PublicKey) ([]byte, error) { + m := map[int64]interface{}{ + commoncose.KeyTypeLabel: int64(commoncose.KeyTypeEC2), + commoncose.AlgorithmLabel: -7, // ES256 (ECDSA w/ SHA-256) + commoncose.ECCurveLabel: 1, // P-256 + commoncose.ECXLabel: key.X.Bytes(), + commoncose.ECYLabel: key.Y.Bytes(), + } + return cbor.Marshal(m) +} + +func decodeECDSAPrivateKey( + data []byte, + expectedStandardCurve ...string, +) (*ecdsa.PrivateKey, error) { + var m map[int64]interface{} + if err := cbor.Unmarshal(data, &m); err != nil { + return nil, err + } + publicKey, err := decodeECDSAPublicKeyFromMap(m, expectedStandardCurve...) + if err != nil { + return nil, fmt.Errorf("failed to decode public key from map: %w", err) + } + + d := big.NewInt(0) + d.SetBytes(m[commoncose.ECDLabel].([]byte)) + + privateKey := &ecdsa.PrivateKey{ + PublicKey: *publicKey, + D: d, + } + return privateKey, nil +} + +func decodeECDSAPublicKey( + data []byte, + expectedStandardCurve ...string, +) (*ecdsa.PublicKey, error) { + var m map[int64]interface{} + if err := cbor.Unmarshal(data, &m); err != nil { + return nil, err + } + return decodeECDSAPublicKeyFromMap(m, expectedStandardCurve...) +} + +func decodeECDSAPublicKeyFromMap( + m map[int64]interface{}, + expectedStandardCurve ...string, +) (*ecdsa.PublicKey, error) { + ecKey, err := commoncose.NewECCoseKey(m) + if err != nil { + return nil, fmt.Errorf("failed to get public key from COSE key: %w", err) + } + + genericKey, err := ecKey.PublicKey() + if err != nil { + return nil, fmt.Errorf("failed to get public key from COSE key: %w", err) + } + + publicKey, ok := genericKey.(*ecdsa.PublicKey) + if !ok { + return nil, fmt.Errorf("expected ECDSA public key, got %T", genericKey) + } + + if len(expectedStandardCurve) > 0 && + publicKey.Params().Name != expectedStandardCurve[0] { + return nil, fmt.Errorf("expected ECDSA public key with curve %s, got %s", + expectedStandardCurve[0], publicKey.Curve.Params().Name) + } + + return publicKey, nil +} + +func WriteCoseECDSAPrivateKey( + fileName string, + privateKey *ecdsa.PrivateKey, + perms ...os.FileMode, +) ([]byte, error) { + var err error + var data []byte + if data, err = encodePrivateKeyToCOSE(privateKey); err != nil { + return nil, err + } + + perm := os.FileMode(ECDSAPrivateDefaultPerm) // Default permission + if len(perms) > 0 { + perm = perms[0] + } + + // Save to file + if err := os.WriteFile(fileName, data, perm); err != nil { + return nil, err + } + return data, nil +} + +func WriteCoseECDSAPublicKey( + fileName string, + publicKey *ecdsa.PublicKey, + perms ...os.FileMode, +) ([]byte, error) { + var err error + var data []byte + if data, err = encodePublicKeyToCOSE(publicKey); err != nil { + return nil, err + } + + perm := os.FileMode(ECDSAPublicDefaultPerm) // Default permission + if len(perms) > 0 { + perm = perms[0] + } + + // Save to file + if err := os.WriteFile(fileName, data, perm); err != nil { + return nil, err + } + return data, nil +} diff --git a/sealerpubkey.go b/keyio/sealerpubkey.go similarity index 99% rename from sealerpubkey.go rename to keyio/sealerpubkey.go index b60f2f3..bc33f30 100644 --- a/sealerpubkey.go +++ b/keyio/sealerpubkey.go @@ -1,4 +1,4 @@ -package veracity +package keyio import ( "crypto/ecdsa" @@ -49,7 +49,6 @@ func DecodeECDSAPublicPEM(data []byte) (*ecdsa.PublicKey, error) { // material presented as a single, base64 encoded, string. This is typically // more convenient for command line and environment vars func DecodeECDSAPublicString(data string) (*ecdsa.PublicKey, error) { - keyData, err := base64.StdEncoding.DecodeString(data) if err != nil { return nil, err diff --git a/localreader.go b/localmassifs/localreader.go similarity index 74% rename from localreader.go rename to localmassifs/localreader.go index a5049cb..b5612dc 100644 --- a/localreader.go +++ b/localmassifs/localreader.go @@ -1,4 +1,4 @@ -package veracity +package localmassifs import ( "bufio" @@ -6,6 +6,7 @@ import ( "io" "os" "path/filepath" + "strings" "github.com/datatrails/go-datatrails-merklelog/massifs" ) @@ -49,11 +50,17 @@ func (o *StdinOpener) Open(string) (io.ReadCloser, error) { // Utilities to remove the os dependencies from the MassifReader type OsDirLister struct{} +type SuffixDirLister struct { + OsDirLister + Suffix string +} func NewDirLister() massifs.DirLister { return &OsDirLister{} } - +func NewSuffixDirLister(suffix string) massifs.DirLister { + return &SuffixDirLister{Suffix: suffix} +} func (*OsDirLister) ListFiles(name string) ([]string, error) { dpath, err := filepath.Abs(name) if err != nil { @@ -72,3 +79,17 @@ func (*OsDirLister) ListFiles(name string) ([]string, error) { } return result, nil } + +func (s *SuffixDirLister) ListFiles(name string) ([]string, error) { + found, err := s.OsDirLister.ListFiles(name) + if err != nil { + return nil, err + } + var matched []string + for _, f := range found { + if strings.HasSuffix(f, s.Suffix) { + matched = append(matched, f) + } + } + return matched, nil +} diff --git a/localwriter.go b/localmassifs/localwriter.go similarity index 98% rename from localwriter.go rename to localmassifs/localwriter.go index 9d63494..f91debb 100644 --- a/localwriter.go +++ b/localmassifs/localwriter.go @@ -1,4 +1,4 @@ -package veracity +package localmassifs import ( "io" diff --git a/localmassifs/readverified.go b/localmassifs/readverified.go new file mode 100644 index 0000000..443f702 --- /dev/null +++ b/localmassifs/readverified.go @@ -0,0 +1,99 @@ +// Package localmassifs provides functionality to read and verify massifs from a local filesystem. +package localmassifs + +import ( + "context" + "fmt" + + "github.com/datatrails/go-datatrails-common/cbor" + "github.com/datatrails/go-datatrails-common/logger" + "github.com/datatrails/go-datatrails-merklelog/massifs" +) + +type ReaderOptions struct { + log logger.Logger + MassifsDir string + SealsDir string + MassifHeight uint8 + CBORCodec cbor.CBORCodec +} + +func NewReaderDefaultConfig(log logger.Logger, massifsDir, sealsDir string) (ReaderOptions, error) { + + var err error + options := ReaderOptions{ + MassifsDir: massifsDir, + SealsDir: sealsDir, + MassifHeight: 14, + } + if options.CBORCodec, err = massifs.NewRootSignerCodec(); err != nil { + return ReaderOptions{}, err + } + return options, nil +} + +// ReadVerifiedHeadMassif reads the verified head massif from the specified directory +func ReadVerifiedHeadMassif(options ReaderOptions, baseOpts ...massifs.DirCacheOption) (*massifs.VerifiedContext, error) { + + var err error + + opts := []massifs.DirCacheOption{ + massifs.WithDirCacheMassifLister(NewSuffixDirLister(".log")), + massifs.WithDirCacheSealLister(NewSuffixDirLister(".sth")), + massifs.WithReaderOption(massifs.WithMassifHeight(options.MassifHeight)), + massifs.WithReaderOption(massifs.WithCBORCodec(options.CBORCodec)), + } + opts = append(opts, baseOpts...) + + cache, err := massifs.NewLogDirCache(options.log, NewFileOpener(), opts...) + if err != nil { + return nil, err + } + + reader, err := massifs.NewLocalReader(options.log, cache) + if err != nil { + return nil, err + } + + cache.ReplaceOptions(opts...) + + massifCache, err := cache.ReadMassifDirEntry(options.MassifsDir) + if err != nil { + return nil, fmt.Errorf("failed to read massif dir entry: %w", err) + } + massifInfo := massifCache.GetInfo() + fmt.Printf("Read massif %d to %d from %s\n", massifInfo.FirstMassifIndex, massifInfo.HeadMassifIndex, massifInfo.Directory) + + var sealCache massifs.DirCacheEntry + + if options.MassifsDir == options.SealsDir { + sealCache = massifCache + err = cache.FindSealFiles(options.MassifsDir) + } else { + sealCache, err = cache.ReadSealDirEntry(options.SealsDir) + if err != nil { + return nil, fmt.Errorf("failed to read massif dir entry: %w", err) + } + } + + sealInfo := sealCache.GetInfo() + fmt.Printf("Read seals for massifs %d to %d from %s\n", sealInfo.FirstSealIndex, sealInfo.HeadSealIndex, sealInfo.Directory) + if err != nil { + return nil, fmt.Errorf("failed to read massif dir entry: %w", err) + } + + mc, err := cache.ReadMassif(massifInfo.Directory, uint64(massifInfo.HeadMassifIndex)) + if err != nil { + return nil, fmt.Errorf("failed to read massif %d from %s: %w", massifInfo.HeadMassifIndex, massifInfo.Directory, err) + } + + seal, err := cache.ReadSeal(sealInfo.Directory, uint64(massifInfo.HeadMassifIndex)) + if err != nil { + return nil, fmt.Errorf("failed to read seal for massif %d from %s: %w", massifInfo.HeadMassifIndex, sealInfo.Directory, err) + } + verified, err := reader.VerifyContext(context.Background(), *mc, massifs.WithCheckpoint(&seal.Sign1Message, &seal.MMRState)) + if err != nil { + return nil, fmt.Errorf("failed to read massif %d from %s: %w", massifInfo.HeadMassifIndex, massifInfo.Directory, err) + } + return verified, nil +} diff --git a/mmriver/const.go b/mmriver/const.go new file mode 100644 index 0000000..a79754f --- /dev/null +++ b/mmriver/const.go @@ -0,0 +1,6 @@ +package mmriver + +const ( + LeafTypePlain = uint8(0) + expectedExtraBytesSize = 24 +) diff --git a/mmriver/mmrentryversion1.go b/mmriver/mmrentryversion1.go new file mode 100644 index 0000000..bbf3233 --- /dev/null +++ b/mmriver/mmrentryversion1.go @@ -0,0 +1,82 @@ +// Package mmriver works with the datatrails ledger based on draft-bryce-cose-receipts-mmr-profile +package mmriver + +import ( + "crypto/sha256" + "encoding/binary" + "errors" +) + +// MMREntryVersion1 gets the mmr entry for log entry version 1. +// mmr entry format for log entry version 1: +// +// H( domain | mmrSalt | serializedBytes ) +// +// where mmrSalt = extraBytes + idtimestamp +// +// NOTE: extraBytes is consistently 24 bytes on the trie value, so we pad/truncate extrabytes here +// to ensure its 24 bytes also. This allows greater consistency and ease of moving between mmrSalt and trieValue +func MMREntryVersion1(extraBytes []byte, idtimestamp uint64, serializedBytes []byte) ([]byte, error) { + hasher := sha256.New() + + // domain + hasher.Write([]byte{byte(LeafTypePlain)}) + + // mmrSalt + + // ensure extrabytes is 24 bytes long + extraBytes, err := ConsistentExtraBytesSize(extraBytes) + if err != nil { + return nil, err + } + hasher.Write(extraBytes) + + // convert idtimestamp to bytes + idTimestampBytes := make([]byte, 8) + binary.BigEndian.PutUint64(idTimestampBytes, idtimestamp) + hasher.Write(idTimestampBytes) + + // serializedBytes + hasher.Write(serializedBytes) + + return hasher.Sum(nil), nil +} + +func TrimExtraBytes(extraBytes []byte) []byte { + extraBytesSize := len(extraBytes) + + // larger size need to truncate + if extraBytesSize > expectedExtraBytesSize { + extraBytes = extraBytes[:expectedExtraBytesSize] + } + + // smaller size need to pad + if extraBytesSize < expectedExtraBytesSize { + tmp := make([]byte, expectedExtraBytesSize) + copy(tmp[:extraBytesSize], extraBytes) + return tmp + } + + // goldilocks just right + return extraBytes +} + +// consistentExtraBytesSize ensures the given extraBytes is padded/truncated to exactly 24 bytes +func ConsistentExtraBytesSize(extraBytes []byte) ([]byte, error) { + extraBytesSize := len(extraBytes) + + // larger size need to truncate + if extraBytesSize > expectedExtraBytesSize { + return nil, errors.New("extra bytes is too large, maximum extra bytes size is 24") + } + + // smaller size need to pad + if extraBytesSize < expectedExtraBytesSize { + tmp := make([]byte, expectedExtraBytesSize) + copy(tmp[:extraBytesSize], extraBytes) + return tmp, nil + } + + // goldilocks just right + return extraBytes, nil +} diff --git a/replicatelogs.go b/replicatelogs.go index f36f576..1bfb401 100644 --- a/replicatelogs.go +++ b/replicatelogs.go @@ -17,6 +17,8 @@ import ( "github.com/datatrails/go-datatrails-merklelog/massifs" "github.com/datatrails/go-datatrails-merklelog/massifs/watcher" "github.com/datatrails/go-datatrails-merklelog/mmr" + "github.com/datatrails/veracity/keyio" + "github.com/datatrails/veracity/localmassifs" "github.com/gosuri/uiprogress" "github.com/urfave/cli/v2" "golang.org/x/exp/rand" @@ -179,7 +181,6 @@ By default transient errors are re-tried without limit, and if the error is 429, // replicateChanges replicate the changes for the provided slice of tenants. // Paralelism is limited by breaking the total changes into smaller slices and calling this function func replicateChanges(cCtx *cli.Context, cmd *CmdCtx, changes []TenantMassif, progress Progresser) error { - var wg sync.WaitGroup errChan := make(chan error, len(changes)) // buffered so it doesn't block @@ -249,7 +250,6 @@ func replicateChanges(cCtx *cli.Context, cmd *CmdCtx, changes []TenantMassif, pr } func initReplication(cCtx *cli.Context, cmd *CmdCtx, change TenantMassif) (*VerifiedReplica, uint32, uint32, error) { - replicator, err := NewVerifiedReplica(cCtx, cmd.Clone()) if err != nil { return nil, 0, 0, err @@ -268,7 +268,6 @@ func defaultRetryDelay(_ error) time.Duration { } func newProgressor(cCtx *cli.Context, barName string, increments int) Progresser { - if !cCtx.Bool("progress") { return NewNoopProgress() } @@ -292,7 +291,6 @@ type VerifiedReplica struct { func NewVerifiedReplica( cCtx *cli.Context, cmd *CmdCtx, ) (*VerifiedReplica, error) { - dataUrl := cCtx.String("data-url") reader, err := cfgReader(cmd, cCtx, dataUrl == "") if err != nil { @@ -307,7 +305,7 @@ func NewVerifiedReplica( return nil, fmt.Errorf("massif height must be less than 256") } - cache, err := massifs.NewLogDirCache(logger.Sugar, NewFileOpener()) + cache, err := massifs.NewLogDirCache(logger.Sugar, localmassifs.NewFileOpener()) if err != nil { return nil, err } @@ -318,8 +316,8 @@ func NewVerifiedReplica( opts := []massifs.DirCacheOption{ massifs.WithDirCacheReplicaDir(cCtx.String("replicadir")), - massifs.WithDirCacheMassifLister(NewDirLister()), - massifs.WithDirCacheSealLister(NewDirLister()), + massifs.WithDirCacheMassifLister(localmassifs.NewDirLister()), + massifs.WithDirCacheSealLister(localmassifs.NewDirLister()), massifs.WithReaderOption(massifs.WithMassifHeight(uint8(massifHeight))), massifs.WithReaderOption(massifs.WithSealGetter(&localReader)), massifs.WithReaderOption(massifs.WithCBORCodec(cmd.cborCodec)), @@ -330,7 +328,7 @@ func NewVerifiedReplica( // verification will fail with a suitable error. pemString := cCtx.String("sealer-key") if pemString != "" { - pem, err := DecodeECDSAPublicString(pemString) + pem, err := keyio.DecodeECDSAPublicString(pemString) if err != nil { return nil, err } @@ -349,7 +347,7 @@ func NewVerifiedReplica( return &VerifiedReplica{ cCtx: cCtx, log: logger.Sugar, - writeOpener: NewFileWriteOpener(), + writeOpener: localmassifs.NewFileWriteOpener(), localReader: &localReader, remoteReader: &remoteReader, rootReader: &cmd.rootReader, @@ -364,8 +362,8 @@ func NewVerifiedReplica( // interesting. func (v *VerifiedReplica) ReplicateVerifiedUpdates( ctx context.Context, - tenantIdentity string, startMassif, endMassif uint32) error { - + tenantIdentity string, startMassif, endMassif uint32, +) error { isNilOrNotFound := func(err error) bool { if err == nil { return true @@ -381,7 +379,6 @@ func (v *VerifiedReplica) ReplicateVerifiedUpdates( // on demand promotion of a v0 state to a v1 state, for compatibility with the consistency check. trustedBaseState := func(local *massifs.VerifiedContext) (massifs.MMRState, error) { - if local.MMRState.Version > int(massifs.MMRStateVersion0) { return local.MMRState, nil } @@ -447,7 +444,6 @@ func (v *VerifiedReplica) ReplicateVerifiedUpdates( // dealt with, local is always the predecessor. if local != nil { - // Start from the next massif after the last verified massif and do not // re-verify massifs we have already verified and replicated, if startMassif > local.Start.MassifIndex+1 { @@ -525,8 +521,8 @@ func (v *VerifiedReplica) ReplicateVerifiedUpdates( // This method has no side effects in the case where the remote and the local // are verified to be identical, the original local instance is retained. func (v *VerifiedReplica) replicateVerifiedContext( - local *massifs.VerifiedContext, remote *massifs.VerifiedContext) (*massifs.VerifiedContext, error) { - + local *massifs.VerifiedContext, remote *massifs.VerifiedContext, +) (*massifs.VerifiedContext, error) { if local == nil { return nil, v.localReader.ReplaceVerifiedContext(remote, v.writeOpener) } @@ -573,7 +569,6 @@ func (v *VerifiedReplica) replicateVerifiedContext( } func verifiedStateEqual(a *massifs.VerifiedContext, b *massifs.VerifiedContext) bool { - var err error // There is no difference in the log format between the two versions currently supported. @@ -661,7 +656,6 @@ func newWatchConfig(cCtx *cli.Context, cmd *CmdCtx) (WatchConfig, error) { } func readTenantMassifChanges(ctx context.Context, cCtx *cli.Context, cmd *CmdCtx) ([]TenantMassif, error) { - if cCtx.IsSet("latest") { // This is because people get tripped up with the `veracity watch -z 90000h | veracity replicate-logs` idiom, // Its such a common use case that we should just make it work. @@ -704,7 +698,6 @@ func readTenantMassifChanges(ctx context.Context, cCtx *cli.Context, cmd *CmdCtx } func NewPrefetchingSealReader(ctx context.Context, sealGetter massifs.SealGetter, tenantIdentity string, massifIndex uint32) (*prefetchingSealReader, error) { - msg, state, err := sealGetter.GetSignedRoot(ctx, tenantIdentity, massifIndex) if err != nil { return nil, err diff --git a/scitt/mandatory.go b/scitt/mandatory.go new file mode 100644 index 0000000..78d3331 --- /dev/null +++ b/scitt/mandatory.go @@ -0,0 +1,149 @@ +package scitt + +import ( + "errors" + "fmt" + + "github.com/datatrails/go-datatrails-common/cose" + commoncose "github.com/datatrails/go-datatrails-common/cose" +) + +const ( + ProblemTitleServiceSpecific = "Service Specific" + ProblemTitleOperationNotFound = "Operation Not Found" + ProblemTitleOperationFailed = "Operation Failed" + ProblemTitleTransient = "Transient Service Issue" + ProblemTitleRejected = "Rejected" + ProblemTitleToManyRequests = "To Many Requests" + ProblemTitleConfirmationMissing = "Confirmation Missing" + ProblemInstanceRejectedByRegistrationPolicy = "urn:ietf:params:scitt:error:signed-statement:rejected-by-registration-policy" + ProblemInstanceConfirmationMissing = "urn:ietf:params:scitt:error:signed-statement:confirmation-missing" + ProblemInstanceToManyRequests = "urn:ietf:params:scitt:error:tooManyRequests" + ProblemInstanceTransientAndInternal = "urn:ietf:params:scitt:error:transient-and-internal" + ProblemInstanceServiceSpecific = "urn:ietf:params:scitt:error:service-specific" + ProblemInstanceNotFound = "urn:ietf:params:scitt:error:notFound" +) + +// mandatory checks required of any transparency service on registration + +type CheckedStatement struct { + Claims *cose.CWTClaims + Statement *cose.CoseSign1Message +} + +type RegistrationPolicy struct { + RequireCNFPublic bool + // We do not support x509 verification at this time + RequireX509 bool + AllowUnverified bool +} + +// RegistrationPolicyUnverified returns a RegistrationPolicy that allows unverified statements. +// And can be used to obtain decoded statements that otherwise pass the mandatory checks. +func RegistrationPolicyUnverified() RegistrationPolicy { + return RegistrationPolicy{ + RequireCNFPublic: false, + RequireX509: false, + AllowUnverified: true, + } +} + +func RegistrationPolicyVerified() RegistrationPolicy { + return RegistrationPolicy{ + RequireCNFPublic: true, + RequireX509: false, + AllowUnverified: false, + } +} + +func RegistrationMandatoryChecks( + signedStatement []byte, + policy RegistrationPolicy, +) (CheckedStatement, *ConciseProblemDetails) { + if policy.RequireX509 { + return CheckedStatement{}, &ConciseProblemDetails{ + Title: ProblemTitleRejected, + Detail: "Signed Statement not accepted by the current Registration Policy. X509 verification is not supported", + Instance: ProblemInstanceRejectedByRegistrationPolicy, + ResponseCode: CoAPBadRequest, + } + } + + // cbor decode statement + statement, err := commoncose.NewCoseSign1MessageFromCBOR(signedStatement) + + if err != nil { + return CheckedStatement{}, &ConciseProblemDetails{ + Title: ProblemTitleRejected, + Detail: fmt.Sprintf("Signed Statement not accepted by the current Registration Policy. Not a valid COSE Sign1 message: %v", err), + Instance: ProblemInstanceRejectedByRegistrationPolicy, + ResponseCode: CoAPBadRequest, + } + } + // Begin: Mandatory Registration checks + + // verify cose_sign1 message: + // + // Per - https://ietf-wg-scitt.github.io/draft-ietf-scitt-architecture/draft-ietf-scitt-architecture.html#section-4.1.1.1 + // Registration "MUST, at a minimum, syntactically check the Issuer of the Signed Statement by cryptographically verifying the COSE signature according to" + + err = statement.VerifyWithCWTPublicKey(nil) + + // if the error is because there is no cwt issuer, ensure we communicate that + if errors.Is(err, commoncose.ErrCWTClaimsNoIssuer) { + return CheckedStatement{}, &ConciseProblemDetails{ + Title: ProblemTitleRejected, + Detail: "Signed Statement not accepted by the current Registration Policy. issuer claim not present in CWT", + Instance: ProblemInstanceRejectedByRegistrationPolicy, + ResponseCode: CoAPBadRequest, + } + } + + if errors.Is(err, commoncose.ErrCWTClaimsNoSubject) { + return CheckedStatement{}, &ConciseProblemDetails{ + Title: ProblemTitleRejected, + Detail: "Signed Statement not accepted by the current Registration Policy. subject claim not present in CWT", + Instance: ProblemInstanceRejectedByRegistrationPolicy, + ResponseCode: CoAPBadRequest, + } + } + + // if the error is because there is no cwt verification key, ensure we communicate that + if errors.Is(err, commoncose.ErrCWTClaimsNoCNF) { + + if policy.RequireCNFPublic || !policy.AllowUnverified { + return CheckedStatement{}, &ConciseProblemDetails{ + Title: ProblemTitleConfirmationMissing, + Detail: fmt.Sprintf("Signed Statement did not contain proof of possession: %v", err), + Instance: ProblemInstanceConfirmationMissing, + ResponseCode: CoAPBadRequest, + } + } + err = nil + } + + if err != nil { + return CheckedStatement{}, &ConciseProblemDetails{ + Title: ProblemTitleRejected, + Detail: fmt.Sprintf("Signed Statement not accepted by the current Registration Policy. Verification failed: %v", err), + Instance: ProblemInstanceRejectedByRegistrationPolicy, + ResponseCode: CoAPBadRequest, + } + } + + cwtClaims, err := statement.CWTClaimsFromProtectedHeader() + if err != nil { + return CheckedStatement{}, &ConciseProblemDetails{ + Title: ProblemTitleRejected, + Detail: fmt.Sprintf("Signed Statement not accepted by the current Registration Policy. CWT Claims missing or invalid: %v", err), + Instance: ProblemInstanceRejectedByRegistrationPolicy, + ResponseCode: CoAPBadRequest, + } + } + // END: Mandatory Registration checks + + return CheckedStatement{ + Claims: cwtClaims, + Statement: statement, + }, nil +} diff --git a/scitt/rfc9290.go b/scitt/rfc9290.go new file mode 100644 index 0000000..fcaf636 --- /dev/null +++ b/scitt/rfc9290.go @@ -0,0 +1,120 @@ +package scitt + +import ( + "github.com/fxamacker/cbor/v2" +) + +// public scitt support for https://www.rfc-editor.org/rfc/rfc9290.html + +const ( + RFC9290MediaType = "application/concise-problem-details+cbor" +) + +const ( + + // success coap codes https://www.rfc-editor.org/rfc/rfc7252#section-12.1.2 + CoAPCreated = 201 + CoAPDeleted = 202 + CoAPValid = 203 + CoAPChanged = 204 + CoAPContent = 205 + + // non-success coap codes per https://www.rfc-editor.org/rfc/rfc7252#section-12.1.2 + + CoAPBadRequest = 400 + CoAPUnauthorized = 401 + CoAPBadOption = 402 + CoAPForbidden = 403 + CoAPNotFound = 404 + CoAPMethodNotAllowed = 405 + CoAPNotAcceptable = 406 + CoAPPreConditionFailed = 412 + CoAPRequestEntityToLarge = 413 + CoAPUnsupportedContentFormat = 415 + CoAPInternalServerError = 500 + CoAPNotImplemented = 501 + CoAPBadGateway = 502 + CoAPServiceUnavailable = 503 + CoAPGatewayTimeout = 504 + CoAPProxyingNotSupported = 505 +) + +var ( + // Note: this value is established by code in the test TestProblemDetailsWriteResponseError + ProblemDetailsEncodingError = []byte{ + 163, 32, 116, 101, 114, 114, 111, 114, 32, 101, 110, 99, 111, 100, + 105, 110, 103, 32, 101, 114, 114, 111, 114, 33, 120, 58, 84, 104, + 105, 115, 32, 105, 115, 32, 97, 32, 115, 101, 114, 118, 101, 114, + 32, 101, 114, 114, 111, 114, 32, 101, 110, 99, 111, 100, 105, 110, + 103, 32, 116, 104, 101, 32, 112, 114, 111, 98, 108, 101, 109, 32, + 100, 101, 116, 97, 105, 108, 115, 32, 105, 116, 115, 101, 108, 102, + 35, 25, 1, 244, + } + + problemDetailsEncodingError = ConciseProblemDetails{ + Title: "error encoding error", + Detail: "This is a server error encoding the problem details itself", + ResponseCode: CoAPInternalServerError, + } + + CoAPResponseCodes = map[uint]bool{ + CoAPCreated: true, + CoAPDeleted: true, + CoAPValid: true, + CoAPChanged: true, + CoAPContent: true, + CoAPBadRequest: true, + CoAPUnauthorized: true, + CoAPBadOption: true, + CoAPForbidden: true, + CoAPNotFound: true, + CoAPMethodNotAllowed: true, + CoAPNotAcceptable: true, + CoAPPreConditionFailed: true, + CoAPRequestEntityToLarge: true, + CoAPUnsupportedContentFormat: true, + CoAPInternalServerError: true, + CoAPNotImplemented: true, + CoAPBadGateway: true, + CoAPServiceUnavailable: true, + CoAPGatewayTimeout: true, + CoAPProxyingNotSupported: true, + } +) + +// ConciseProblemDetails encodes information about an error according to RFC 9260 +// See https://www.rfc-editor.org/rfc/rfc9290.html +type ConciseProblemDetails struct { + Title string `cbor:"-1,keyasint,omitempty"` + Detail string `cbor:"-2,keyasint,omitempty"` + Instance string `cbor:"-3,keyasint,omitempty"` + ResponseCode uint64 `cbor:"-4,keyasint,omitempty"` + BaseUri string `cbor:"-5,keyasint,omitempty"` + BaseRtl string `cbor:"-6,keyasint,omitempty"` +} + +func (p ConciseProblemDetails) MustMarshalCBOR() []byte { + content, err := cbor.Marshal(p) + if err != nil { + content = ProblemDetailsEncodingError + } + + return content +} + +// ProblemDetailsMarshal marshals a problem details from the +// provided arguments If there is an error marshaling the error, a pre encoded +// problem details for that situation is returned +func ProblemDetailsMarshal(title, detail string, responseCode uint64) []byte { + problem := ConciseProblemDetails{ + Title: title, + Detail: detail, + ResponseCode: responseCode, + } + content, err := cbor.Marshal(&problem) + if err != nil { + content = ProblemDetailsEncodingError + } + + return content +} diff --git a/scitt/scitt.go b/scitt/scitt.go new file mode 100644 index 0000000..c815eb2 --- /dev/null +++ b/scitt/scitt.go @@ -0,0 +1,79 @@ +// Package scitt signed statement conveniences +package scitt + +import ( + "crypto/sha256" + "errors" + "fmt" + "os" + + "github.com/datatrails/veracity/mmriver" +) + +const ( + ExtraBytesSize = 24 +) + +// MMRStatement prepares the details necessary for registering a signed +// statement on a localy forked datatrails ledger replica +type MMRStatement struct { + CheckedStatement + // Content is the signed statement raw cbor bytes exactly as read or provide + Content []byte + // Hash is th sha256 hash of the Statement + Hash []byte + // LeafHash is the MMR ledger defined leaf hash that is added to the ledger + LeafHash []byte + // ExtraBytes are the application contribution to the leaf hash. In the case + // of this pseudo scitt support, it is the first 24 bytes of the Hash + ExtraBytes []byte + // The IDTimestamp that contributed to the leaf hash. + IDTimestamp uint64 + MMRIndexLeaf uint64 +} + +type idTimetampGenerator interface { + NextID() (uint64, error) +} + +func NewMMRStatementFromFile(fileName string, idState idTimetampGenerator, policy RegistrationPolicy) (*MMRStatement, *ConciseProblemDetails, error) { + m := &MMRStatement{} + + content, err := os.ReadFile(fileName) + if err != nil { + return nil, nil, fmt.Errorf("failed to read file %s: %w", fileName, err) + } + + var cpd *ConciseProblemDetails + m.CheckedStatement, cpd = RegistrationMandatoryChecks(content, policy) + if cpd != nil { + return nil, cpd, fmt.Errorf("failed mandatory registration checks: %s", cpd.Detail) + } + + m.Content = content + hasher := sha256.New() + n, err := hasher.Write(m.Content) + if err != nil { + return nil, nil, err + } + if n != len(m.Content) { + return nil, nil, errors.New("hashed to few bytes") + } + m.Hash = hasher.Sum(nil) + + // Could use the hash bytes for content addressibility, but its primarily a scitt demo so use subject, but only the first 24 bytes + // m.ExtraBytes = m.Hash[:ExtraBytesSize] + m.ExtraBytes = mmriver.TrimExtraBytes([]byte(m.Claims.Subject)) + + m.IDTimestamp, err = idState.NextID() + if err != nil { + return nil, nil, fmt.Errorf("failed to generate snowflake id: %w", err) + } + // m.IDTimestamp = 0 // XXX: temporary stabilize the hash + + m.LeafHash, err = mmriver.MMREntryVersion1(m.ExtraBytes, m.IDTimestamp, m.Content) + if err != nil { + return nil, nil, err + } + return m, nil, nil +} diff --git a/tests/append/append_test.go b/tests/append/append_test.go new file mode 100644 index 0000000..1d8f391 --- /dev/null +++ b/tests/append/append_test.go @@ -0,0 +1,30 @@ +package append + +import ( + "os" + + "github.com/datatrails/veracity" +) + +// Test that +func (s *AppendCmdSuite) TestAppendCCFSignedStatement() { + // replicaDir := s.T().TempDir() + + var err error + + err = os.Chdir("/Users/robin/Desktop/personal/ietf/data") + s.Require().NoError(err, "should be able to change directory to /Users/robin/Desktop/ietf/data") + + app := veracity.NewApp("tests", true) + veracity.AddCommands(app, true) + + err = app.Run([]string{ + "veracity", + "-t", "tenant/6a009b40-eb55-4159-81f0-69024f89f53c", + // "-l", "v1/mmrs/tenant/6a009b40-eb55-4159-81f0-69024f89f53c/0/massifs/0000000000000000.log", + "-l", "/Users/robin/Desktop/personal/ietf/data", + // "append" , "--generate-sealer-key", + "append", "--sealer-key", "ecdsa-key-private.cbor", "--signed-statement", "in-toto.json.hashenvelope.cose.empty_uhdr", + }) + s.NoError(err) +} diff --git a/tests/append/suite_test.go b/tests/append/suite_test.go new file mode 100644 index 0000000..1541438 --- /dev/null +++ b/tests/append/suite_test.go @@ -0,0 +1,23 @@ +// Package replicatelogs provides a test suite for the ReplicateLogs command. +package append + +import ( + "testing" + + "github.com/datatrails/veracity/tests" + "github.com/stretchr/testify/suite" +) + +type AppendCmdSuite struct { + tests.IntegrationTestSuite +} + +func (s *AppendCmdSuite) SetupSuite() { + s.IntegrationTestSuite.SetupSuite() + // ensure we have the azurite config in the env for all the tests so that --envauth always uses the emulator + s.EnsureAzuriteEnv() +} + +func TestAppendCmdSuite(t *testing.T) { + suite.Run(t, new(AppendCmdSuite)) +} diff --git a/tests/replicatelogs/suite_test.go b/tests/replicatelogs/suite_test.go index 78b4438..771a4d1 100644 --- a/tests/replicatelogs/suite_test.go +++ b/tests/replicatelogs/suite_test.go @@ -1,3 +1,4 @@ +// Package replicatelogs provides a test suite for the ReplicateLogs command. package verifyconsistency import ( @@ -18,6 +19,5 @@ func (s *ReplicateLogsCmdSuite) SetupSuite() { } func TestReplicateLogsCmdSuite(t *testing.T) { - suite.Run(t, new(ReplicateLogsCmdSuite)) } diff --git a/verifyincluded_test.go b/verifyincluded_test.go index ba87bea..dac31c9 100644 --- a/verifyincluded_test.go +++ b/verifyincluded_test.go @@ -20,7 +20,6 @@ import ( // the first entry is a known assetsv2 events // the seconds entry is a known eventsv1 event func testMassifContext(t *testing.T) *massifs.MassifContext { - start := massifs.MassifStart{ MassifHeight: 3, } @@ -80,19 +79,16 @@ type fakeMassifGetter struct { // // one assetsv2 event entry and one eventsv1 entry func NewFakeMassifGetter(t *testing.T) *fakeMassifGetter { - massifContext := testMassifContext(t) return &fakeMassifGetter{ t: t, massifContext: massifContext, } - } // NewFakeMassifGetterInvalidRoot creates a new massif getter that has an incorrect massif root func NewFakeMassifGetterInvalidRoot(t *testing.T) *fakeMassifGetter { - massifContext := testMassifContext(t) // a massif context with 2 entries has its root at index 2 @@ -172,7 +168,6 @@ func TestVerifyAssetsV2Event(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - logTenant, err := test.event.LogTenant() require.Nil(t, err)