diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index ccb6959..88aebdf 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -11,12 +11,12 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: [ '1.18' ] + go: ['1.21' ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} @@ -24,4 +24,4 @@ jobs: run: go build -v ./... - name: Test - run: go test -v ./... + run: go test -v ./... \ No newline at end of file diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 1169dd4..4fdc07a 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -14,18 +14,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: [ '1.18' ] + go: [ '1.21' ] steps: - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v4 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version - version: v1.45.2 + version: v1.57.2 # Optional: working directory, useful for monorepos # working-directory: somedir diff --git a/.golangci.yml b/.golangci.yml index 02ed57d..90e26d0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,35 +1,23 @@ -# This file contains all available configuration options -# with their default values. # options for analysis running run: - # default concurrency is a available CPU number + # Number of operating system threads (`GOMAXPROCS`) that can execute golangci-lint simultaneously. + # If it is explicitly set to 0 (i.e. not the default) then golangci-lint will automatically set the value to match Linux container CPU quota. + # Default: the number of logical CPUs in the machine concurrency: 4 + # timeout for analysis, e.g. 30s, 5m, default is 1m timeout: 3m + # exit code when at least one issue was found, default is 1 issues-exit-code: 1 + # include test files or not, default is true tests: true + # list of build tags, all linters use it. Default is empty list. - build-tags: - # which dirs to skip: issues from them won't be reported; - # can use regexp here: generated.*, regexp is applied on full path; - # default value is empty list, but default dirs are skipped independently - # from this option's value (see skip-dirs-use-default). - # "/" will be replaced by current OS file path separator to properly work - # on Windows. - skip-dirs: - # default is true. Enables skipping of directories: - # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ - skip-dirs-use-default: true - # which files to skip: they will be analyzed, but issues from them - # won't be reported. Default value is empty list, but there is - # no need to include all autogenerated files, we confidently recognize - # autogenerated files. If it's not please let us know. - # "/" will be replaced by current OS file path separator to properly work - # on Windows. - skip-files: - # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": + build-tags: [] + + # If set, we pass it to "go list -mod={option}". From "go help modules": # If invoked with -mod=readonly, the go command is disallowed from the implicit # automatic updating of go.mod described above. Instead, it fails when any changes # to go.mod are needed. This setting is most useful to check that go.mod does @@ -37,20 +25,46 @@ run: # If invoked with -mod=vendor, the go command assumes that the vendor # directory holds the correct copies of dependencies and ignores # the dependency descriptions in go.mod. - #modules-download-mode: readonly|release|vendor + # + # Allowed values: readonly|vendor|mod + # Default: "" + # modules-download-mode: readonly + # Allow multiple parallel golangci-lint instances running. - # If false (default) - golangci-lint acquires file lock on start. + # If false, golangci-lint acquires file lock on start. + # Default: false allow-parallel-runners: true + + # Allow multiple golangci-lint instances running, but serialize them around a lock. + # If false, golangci-lint exits with an error if it fails to acquire file lock on start. + # Default: false + allow-serial-runners: true + + go: '1.21' + # output configuration options output: # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" - format: colored-line-number + formats: + - format: "colored-line-number" + path: stdout + # print lines of code with issue, default is true print-issued-lines: true # print linter name in the end of issue text, default is true print-linter-name: true # make issues output unique by line, default is true uniq-by-line: true + + # Sort results by the order defined in `sort-order`. + # Default: false + sort-results: true + sort-order: + - linter + - severity + - file # filepath, line, and column. + show-stats: true + # all available settings of specific linters linters-settings: errcheck: @@ -67,26 +81,22 @@ linters-settings: # path to a file containing a list of functions to exclude from checking # see https://github.com/kisielk/errcheck#excluding-functions for details #exclude: /path/to/file.txt - funlen: - lines: 60 - statements: 40 - gocognit: - # minimal code complexity to report, 30 by default (but we recommend 10-20) - min-complexity: 10 - nestif: - # minimal complexity of if statements to report, 5 by default - min-complexity: 4 + goconst: # minimal length of string constant, 3 by default - min-len: 3 - # minimal occurrences count to trigger, 3 by default + min-len: 5 + # Minimum occurrences of constant string count to trigger issue, 3 by default min-occurrences: 3 + # Exclude strings matching the given regular expression. + # Default: "" + ignore-strings: "get|post|put|delete|patch|options|head" + gocritic: # Which checks should be enabled; can't be combined with 'disabled-checks'; # See https://go-critic.github.io/overview#checks-overview # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` # By default list of stable checks is used. - enabled-checks: + enabled-checks: [] #- rangeValCopy # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty disabled-checks: @@ -94,17 +104,31 @@ linters-settings: # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks. # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". enabled-tags: + - diagnostic - performance disabled-tags: - experimental - settings: # settings passed to gocritic + + # settings passed to gocritic + # The settings key is the name of a supported gocritic checker. + # The list of supported checkers can be find in https://go-critic.github.io/overview. + settings: captLocal: # must be valid enabled check name + # Whether to restrict checker to params only. paramsOnly: true rangeValCopy: - sizeThreshold: 32 - gocyclo: - # minimal code complexity to report, 30 by default (but we recommend 10-20) - min-complexity: 10 + # Size in bytes that makes the warning trigger. Default: 128 + # This size shoulb be smaller + sizeThreshold: 512 + hugeParam: + # Size in bytes that makes the warning trigger. Default: 80 + # This size shoulb be smaller + sizeThreshold: 512 + ifElseChain: + # Min number of if-else blocks that makes the warning trigger. + # Default: 2 + minThreshold: 4 + godox: # report any comments starting with keywords, this is useful for TODO or FIXME comments that # might be left in the code accidentally and should be resolved before merging @@ -116,167 +140,184 @@ linters-settings: #- OPTIMIZE # marks code that should be optimized before merging #- HACK # marks hack-arounds that should be removed before merging - XXX # Fatal! Important problem + gofmt: # simplify code: gofmt with `-s` option, true by default simplify: true - goimports: - # put imports beginning with prefix after 3rd-party packages; - # it's a comma-separated list of prefixes - local-prefixes: github.com/org/project - golint: - # minimal confidence for issues, default is 0.8 - min-confidence: 0.8 - gomnd: - settings: - mnd: - # the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description. - checks: argument,case,condition,operation,return,assign - gomodguard: - allowed: - modules: # List of allowed modules - # - gopkg.in/yaml.v2 - domains: # List of allowed module domains - # - golang.org - blocked: - modules: # List of blocked modules - # - github.com/uudashr/go-module: # Blocked module - # recommendations: # Recommended modules that should be used instead (Optional) - # - golang.org/x/mod - # reason: "`mod` is the official go.mod parser library." # Reason why the recommended module should be used (Optional) - versions: # List of blocked module version constraints - # - github.com/mitchellh/go-homedir: # Blocked module with version constraint - # version: "< 1.1.0" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons - # reason: "testing if blocked version constraint works." # Reason why the version constraint exists. (Optional) + govet: - # report about shadowed variables - check-shadowing: true + enable-all: false + # enable or disable analyzers by name + enable: + - atomicalign + - shadow + - printf + # settings per analyzer settings: - printf: # analyzer name, run `go tool vet help` to see all analyzers + # analyzer name, run `go tool vet help` to see all analyzers + printf: funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf - # enable or disable analyzers by name - enable: - - atomicalign - enable-all: false - disable: - - shadow - disable-all: false + shadow: + # Whether to be strict about shadowing; can be noisy. + # Default: false + strict: true + depguard: - list-type: blacklist - include-go-root: false - packages: - - github.com/sirupsen/logrus - packages-with-error-message: - # specify an error message to output when a blacklisted package is used - - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" + rules: + # Name of a rule. + main: + # Used to determine the package matching priority. + # There are three different modes: `original`, `strict`, and `lax`. + # Default: "original" + list-mode: original + deny: + - pkg: github.com/sirupsen/logrus + desc: "logging is allowed only by logutils.Log" + lll: # max line length, lines longer will be reported. Default is 120. # '\t' is counted as 1 character by default, and can be changed with the tab-width option line-length: 120 # tab width in spaces. Default to 1. tab-width: 1 - maligned: - # print struct with more effective memory layout or not, false by default - suggest-new: true + nakedret: # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 max-func-lines: 30 + testpackage: # regexp pattern to skip files skip-regexp: (export|internal)_test\.go + unused: - # treat code as a program (not a library) and report unused exported identifiers; default is false. - # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find funcs usages. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: false + # Mark all struct fields that have been written to as used. + # Default: true + field-writes-are-uses: true + # Treat IncDec statement (e.g. `i++` or `i--`) as both read and write operation instead of just write. + # Default: false + post-statements-are-reads: true + # Mark all exported identifiers as used. + # Default: true + exported-is-used: true + # Mark all exported fields as used. + # default: true + exported-fields-are-used: true + # Mark all function parameters as used. + # default: true + parameters-are-used: true + # Mark all local variables as used. + # default: true + local-variables-are-used: true + # Mark all identifiers inside generated files as used. + # Default: true + generated-is-used: true + whitespace: multi-if: false # Enforces newlines (or comments) after every multi-line if statement multi-func: false # Enforces newlines (or comments) after every multi-line function signature + gci: sections: - standard - default - prefix(github.com/free5gc) - section-separators: - - newLine + + # Skip generated files. + # Default: true + skip-generated: true + + # Enable custom order of sections. + # If `true`, make the section order the same as the order of `sections`. + # Default: false + custom-order: true + misspell: - #locale: US - ignore-words: - wsl: - # If true append is only allowed to be cuddled if appending value is - # matching variables, fields or types on line above. Default is true. - strict-append: true - # Allow calls and assignments to be cuddled as long as the lines have any - # matching variables, fields or types. Default is true. - allow-assign-and-call: true - # Allow multiline assignments to be cuddled. Default is true. - allow-multiline-assign: true - # Allow declarations (var) to be cuddled. - allow-cuddle-declarations: false - # Allow trailing comments in ending of blocks - allow-trailing-comment: true - # Force newlines in end of case at this limit (0 = never). - force-case-trailing-whitespace: 0 - # Force cuddling of err checks with err var assignment - force-err-cuddling: false - # Allow leading comments to be separated with empty liens - allow-separated-leading-comment: false - custom: + locale: US + ignore-words: [] + + gomnd: + # !important in golangci-lint v1.58.0, gomnd is replaced by mnd + # List of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description. + # Default: ["argument", "case", "condition", "operation", "return", "assign"] + checks: + # - argument + - case + # - condition + - operation + - return + # - assign + # List of numbers to exclude from analysis. + # The numbers should be written as string. + # Values always ignored: "1", "1.0", "0" and "0.0" + # Default: [] + ignored-numbers: [] + # List of file patterns to exclude from analysis. + # Values always ignored: `.+_test.go` + # Default: [] + ignored-files: [] + # List of function patterns to exclude from analysis. + # Following functions are always ignored: `time.Date`, + # `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`, + # `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`. + # Default: [] + ignored-functions: + - 'os\.Mkdir' + - 'os\.MkdirAll' + - '^math\.' + - '^http\.StatusText$' + + # custom: # Each custom linter should have a unique name. linters: + disable-all: true enable: + - errcheck + - goconst + - gocritic + - godox - gofmt - govet - - errcheck + - lll + - nakedret + # - testpackage - staticcheck - unused + - whitespace + - gci + - misspell - gosimple - - structcheck - - varcheck + # - gomnd - ineffassign - - deadcode - typecheck - # Additional - - lll - - godox - # - gomnd - # - goconst - # - gocognit - # - maligned - # - nestif - # - gomodguard - - nakedret - # - golint - - gci - - misspell - gofumpt - - whitespace - unconvert - predeclared - noctx - dogsled - bodyclose - asciicheck - # - stylecheck - # - unparam - # - wsl + - gosec + # - dupl - #disable-all: false + # Enable only fast linters from enabled linters set (first run won't be fast) + # Default: false fast: true + + issues: # List of regexps of issue texts to exclude, empty list by default. # But independently from this option we use default exclude patterns, # it can be disabled by `exclude-use-default: false`. To list all # excluded by default patterns execute `golangci-lint run --help` - exclude: + exclude: [] # Excluding configuration per-path, per-linter, per-text and per-source - exclude-rules: + exclude-rules: [] # Exclude some linters from running on tests files. # Independently from option `exclude` we use default exclude patterns, # it can be disabled by this option. To list all @@ -287,7 +328,7 @@ issues: # regular expressions become case sensitive. exclude-case-sensitive: false # The list of ids of default excludes to include or disable. By default it's empty. - include: + include: [] #- EXC0002 # disable excluding of issues about comments from golint # Maximum issues count per one linter. Set to 0 to disable. Default is 50. #max-issues-per-linter: 0 @@ -304,25 +345,37 @@ issues: new-from-rev: "" # Show only new issues created in git patch with set file path. #new-from-patch: path/to/patch/file + severity: - # Default value is empty string. - # Set the default severity for issues. If severity rules are defined and the issues - # do not match or no severity is provided to the rule this will be the default - # severity applied. Severities should match the supported severity names of the - # selected out format. + # Set the default severity for issues. + # + # If severity rules are defined and the issues do not match or no severity is provided to the rule + # this will be the default severity applied. + # Severities should match the supported severity names of the selected out format. # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity - # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity - # - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message + # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#SeverityLevel + # - GitHub: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message + # - TeamCity: https://www.jetbrains.com/help/teamcity/service-messages.html#Inspection+Instance + # + # `@linter` can be used as severity value to keep the severity from linters (e.g. revive, gosec, ...) + # + # Default: "" default-severity: error + # The default value is false. # If set to true severity-rules regular expressions become case sensitive. case-sensitive: false - # Default value is empty list. - # When a list of severity rules are provided, severity information will be added to lint - # issues. Severity rules have the same filtering capability as exclude rules except you - # are allowed to specify one matcher per severity rule. + + # When a list of severity rules are provided, severity information will be added to lint issues. + # Severity rules have the same filtering capability as exclude rules + # except you are allowed to specify one matcher per severity rule. + # + # `@linter` can be used as severity value to keep the severity from linters (e.g. revive, gosec, ...) + # # Only affects out formats that support setting severity information. + # + # Default: [] rules: - linters: - gomnd - severity: ignore + severity: info diff --git a/go.mod b/go.mod index b73cc0a..edea957 100644 --- a/go.mod +++ b/go.mod @@ -3,42 +3,14 @@ module github.com/free5gc/ike go 1.17 require ( - github.com/antonfisher/nested-logrus-formatter v1.3.0 - github.com/free5gc/logger_conf v1.0.0 - github.com/free5gc/logger_util v1.0.0 - github.com/sirupsen/logrus v1.7.0 - github.com/vishvananda/netlink v1.1.0 + github.com/pkg/errors v0.9.1 + github.com/stretchr/testify v1.8.3 ) require ( - github.com/bytedance/sonic v1.9.1 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect - github.com/free5gc/path_util v1.0.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.9.1 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/kr/pretty v0.1.0 // indirect - github.com/leodido/go-urn v1.2.4 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect - github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/protobuf v1.33.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index a1f4291..173a2fd 100644 --- a/go.sum +++ b/go.sum @@ -1,245 +1,25 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/antonfisher/nested-logrus-formatter v1.3.0 h1:8zixYquU1Odk+vzAaAQPAdRh1ZjmUXNQ1T+dUBvlhVo= -github.com/antonfisher/nested-logrus-formatter v1.3.0/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/free5gc/logger_conf v1.0.0 h1:cMqqB8L4HjE57tP36mBmiVUPIcHv8Ayr12jKfeiwqZU= -github.com/free5gc/logger_conf v1.0.0/go.mod h1:DHecLXVV1qA5Z+lSoUvZdZ6tCtZidTNmtx99jsCfgLE= -github.com/free5gc/logger_util v1.0.0 h1:hLTCTnKlEqJURrBwooNFQLavWPdJzS0o9KGIYUNKdJI= -github.com/free5gc/logger_util v1.0.0/go.mod h1:TK/bAJbm/l2TMNmbsKn83+xUmDNqts69IEir/nCa8w0= -github.com/free5gc/path_util v1.0.0 h1:vJPGTymaWtavz6fJ/7k6WKEYv5BQLAq/O04RP54sab0= -github.com/free5gc/path_util v1.0.0/go.mod h1:OpmcebEKrMPnH7Jg5lZ8y9ZWJNAjQ4l9FGWXUv58Mo0= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= -github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go v1.2.1 h1:dz+JxTe7GZQdErTo7SREc1jQj/hFP1k7jyIAwODoW+k= -github.com/ugorji/go v1.2.1/go.mod h1:cSVypSfTLm2o9fKxXvQgn3rMmkPXovcWor6Qn5tbFmI= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.1/go.mod h1:s/WxCRi46t8rA+fowL40EnmD7ec0XhR7ZypxeBNdzsM= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -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.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -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= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -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= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/ike.go b/ike.go new file mode 100644 index 0000000..af776f6 --- /dev/null +++ b/ike.go @@ -0,0 +1,290 @@ +package ike + +import ( + "crypto/hmac" + + "github.com/pkg/errors" + + "github.com/free5gc/ike/message" + "github.com/free5gc/ike/security" +) + +func EncodeEncrypt( + ikeMsg *message.IKEMessage, + ikesaKey *security.IKESAKey, + role message.Role, +) ([]byte, error) { + if ikesaKey != nil { + err := encryptMsg(ikeMsg, ikesaKey, role) + if err != nil { + return nil, errors.Wrapf(err, "IKE encode encrypt") + } + } + + msg, err := ikeMsg.Encode() + return msg, errors.Wrapf(err, "IKE encode") +} + +// Before use this function, need to use IKEMessage.Encode first +// and get IKESA +func DecodeDecrypt( + msg []byte, + ikeHeader *message.IKEHeader, + ikesaKey *security.IKESAKey, + role message.Role, +) (*message.IKEMessage, error) { + ikeMsg := new(message.IKEMessage) + var err error + + if ikeHeader == nil { + err = ikeMsg.Decode(msg) + if err != nil { + return nil, errors.Wrapf(err, "DecodeDecrypt()") + } + } else { + ikeMsg.IKEHeader = ikeHeader + err = ikeMsg.DecodePayload(msg[message.IKE_HEADER_LEN:]) + if err != nil { + return nil, errors.Wrapf(err, "DecodeDecrypt()") + } + } + + if ikeMsg.Payloads[0].Type() == message.TypeSK { + if ikesaKey == nil { + return nil, errors.Errorf("IKE decode decrypt: need ikesaKey to decrypt") + } + ikeMsg, err = decryptMsg(msg, ikeMsg, ikesaKey, role) + if err != nil { + return nil, errors.Wrapf(err, "IKE decode decrypt") + } + } + + return ikeMsg, nil +} + +func verifyIntegrity( + originData []byte, + checksum []byte, + ikesaKey *security.IKESAKey, + role message.Role, +) error { + expectChecksum, err := calculateIntegrity(ikesaKey, role, originData) + if err != nil { + return errors.Wrapf(err, "verifyIntegrity[%d]", ikesaKey.IntegInfo.TransformID()) + } + + // fmt.Printf("Calculated checksum:\n%s\nReceived checksum:\n%s", + // hex.Dump(expectChecksum), hex.Dump(checksum)) + if !hmac.Equal(checksum, expectChecksum) { + return errors.Errorf("invalid checksum") + } + return nil +} + +func calculateIntegrity( + ikesaKey *security.IKESAKey, + role message.Role, + originData []byte, +) ([]byte, error) { + outputLen := ikesaKey.IntegInfo.GetOutputLength() + + var calculatedChecksum []byte + if role == message.Role_Initiator { + if ikesaKey.Integ_i == nil { + return nil, errors.Errorf("CalcIKEChecksum() : IKE SA have nil Integ_r") + } + ikesaKey.Integ_i.Reset() + if _, err := ikesaKey.Integ_i.Write(originData); err != nil { + return nil, errors.Wrapf(err, "CalcIKEChecksum()") + } + calculatedChecksum = ikesaKey.Integ_i.Sum(nil) + } else { + if ikesaKey.Integ_r == nil { + return nil, errors.Errorf("CalcIKEChecksum() : IKE SA have nil Integ_i") + } + ikesaKey.Integ_r.Reset() + if _, err := ikesaKey.Integ_r.Write(originData); err != nil { + return nil, errors.Wrapf(err, "CalcIKEChecksum()") + } + calculatedChecksum = ikesaKey.Integ_r.Sum(nil) + } + + return calculatedChecksum[:outputLen], nil +} + +func encryptPayload( + plainText []byte, + ikesaKey *security.IKESAKey, + role message.Role, +) ([]byte, error) { + var cipherText []byte + if role == message.Role_Initiator { + var err error + if cipherText, err = ikesaKey.Encr_i.Encrypt(plainText); err != nil { + return nil, errors.Wrapf(err, "encryptPayload()") + } + } else { + var err error + if cipherText, err = ikesaKey.Encr_r.Encrypt(plainText); err != nil { + return nil, errors.Wrapf(err, "encryptPayload()") + } + } + + return cipherText, nil +} + +func decryptPayload( + cipherText []byte, + ikesaKey *security.IKESAKey, + role message.Role, +) ([]byte, error) { + var plainText []byte + if role == message.Role_Initiator { + var err error + if plainText, err = ikesaKey.Encr_r.Decrypt(cipherText); err != nil { + return nil, errors.Wrapf(err, "decryptPayload()") + } + } else { + var err error + if plainText, err = ikesaKey.Encr_i.Decrypt(cipherText); err != nil { + return nil, errors.Wrapf(err, "decryptPayload()") + } + } + + return plainText, nil +} + +func decryptMsg( + msg []byte, + ikeMsg *message.IKEMessage, + ikesaKey *security.IKESAKey, + role message.Role, +) (*message.IKEMessage, error) { + // Check parameters + if ikesaKey == nil { + return nil, errors.Errorf("decryptMsg(): IKE SA is nil") + } + if msg == nil { + return nil, errors.Errorf("decryptMsg(): msg is nil") + } + if ikeMsg == nil { + return nil, errors.Errorf("decryptMsg(): IKE encrypted payload is nil") + } + + // Check if the context contain needed data + if ikesaKey.IntegInfo == nil { + return nil, errors.Errorf("decryptMsg(): No integrity algorithm specified") + } + if ikesaKey.EncrInfo == nil { + return nil, errors.Errorf("decryptMsg(): No encryption algorithm specified") + } + + if ikesaKey.Integ_i == nil { + return nil, errors.Errorf("decryptMsg(): No initiator's integrity key") + } + if ikesaKey.Encr_i == nil { + return nil, errors.Errorf("decryptMsg(): No initiator's encryption key") + } + + var encryptedPayload *message.Encrypted + for _, ikePayload := range ikeMsg.Payloads { + switch ikePayload.Type() { + case message.TypeSK: + encryptedPayload = ikePayload.(*message.Encrypted) + default: + return nil, errors.Errorf( + "Get IKE payload (type %d), this payload will not be decode", + ikePayload.Type()) + } + } + + checksumLength := ikesaKey.IntegInfo.GetOutputLength() + // Checksum + checksum := encryptedPayload.EncryptedData[len(encryptedPayload.EncryptedData)-checksumLength:] + + err := verifyIntegrity(msg[:len(msg)-checksumLength], checksum, ikesaKey, !role) + if err != nil { + return nil, errors.Wrapf(err, "decryptMsg(): verify integrity") + } + + // Decrypt + encryptedData := encryptedPayload.EncryptedData[:len(encryptedPayload.EncryptedData)-checksumLength] + plainText, err := decryptPayload(encryptedData, ikesaKey, role) + if err != nil { + return nil, errors.Wrapf(err, "decryptMsg(): Error decrypting message") + } + + var decryptedPayloads message.IKEPayloadContainer + err = decryptedPayloads.Decode(encryptedPayload.NextPayload, plainText) + if err != nil { + return nil, errors.Wrapf(err, "decryptMsg(): Decoding decrypted payload failed") + } + + ikeMsg.Payloads.Reset() + ikeMsg.Payloads = append(ikeMsg.Payloads, decryptedPayloads...) + return ikeMsg, nil +} + +func encryptMsg( + ikeMsg *message.IKEMessage, + ikesaKey *security.IKESAKey, + role message.Role, +) error { + if ikeMsg == nil { + return errors.Errorf("encryptMsg(): Response IKE message is nil") + } + if ikesaKey == nil { + return errors.Errorf("encryptMsg(): IKE SA is nil") + } + ikePayloads := ikeMsg.Payloads + // Check parameters + if len(ikePayloads) == 0 { + return errors.Errorf("encryptMsg(): No IKE payload to be encrypted") + } + // Check if the context contain needed data + if ikesaKey.IntegInfo == nil { + return errors.Errorf("encryptMsg(): No integrity algorithm specified") + } + if ikesaKey.EncrInfo == nil { + return errors.Errorf("encryptMsg(): No encryption algorithm specified") + } + + if ikesaKey.Integ_r == nil { + return errors.Errorf("encryptMsg(): No responder's integrity key") + } + if ikesaKey.Encr_r == nil { + return errors.Errorf("encryptMsg(): No responder's encryption key") + } + + checksumLength := ikesaKey.IntegInfo.GetOutputLength() + + plainTextPayload, err := ikePayloads.Encode() + if err != nil { + return errors.Wrapf(err, "encryptMsg(): Encoding IKE payload failed.") + } + + // Encrypting + encryptedData, err := encryptPayload(plainTextPayload, ikesaKey, role) + if err != nil { + return errors.Wrapf(err, "encryptMsg(): Error encrypting message") + } + + encryptedData = append(encryptedData, make([]byte, checksumLength)...) + ikeMsg.Payloads.Reset() + sk := ikeMsg.Payloads.BuildEncrypted(ikePayloads[0].Type(), encryptedData) + + // Calculate checksum + ikeMsgData, err := ikeMsg.Encode() + if err != nil { + return errors.Wrapf(err, "encryptMsg(): Encoding IKE message error") + } + checksumOfMessage, err := calculateIntegrity(ikesaKey, role, + ikeMsgData[:len(ikeMsgData)-checksumLength]) + if err != nil { + return errors.Wrapf(err, "encryptMsg(): Error calculating checksum") + } + checksumField := sk.EncryptedData[len(sk.EncryptedData)-checksumLength:] + copy(checksumField, checksumOfMessage) + + return nil +} diff --git a/ike_test.go b/ike_test.go new file mode 100644 index 0000000..7cfe840 --- /dev/null +++ b/ike_test.go @@ -0,0 +1,627 @@ +package ike + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/free5gc/ike/message" + "github.com/free5gc/ike/security" + "github.com/free5gc/ike/security/encr" + "github.com/free5gc/ike/security/integ" +) + +func TestEncodeDecode(t *testing.T) { + encryptionAlgorithm := encr.StrToType("ENCR_AES_CBC_256") + + integrityAlgorithm := integ.StrToType("AUTH_HMAC_SHA1_96") + + ikeSAKey := &security.IKESAKey{ + EncrInfo: encryptionAlgorithm, + IntegInfo: integrityAlgorithm, + } + + var err error + ikeSAKey.SK_ei, err = hex.DecodeString( + "3d7a26417122cee9" + + "c77c59f375b024cd" + + "b9f0b5777ea18b50" + + "f8a671fd3b2daa99") + require.NoError(t, err) + ikeSAKey.Encr_i, err = ikeSAKey.EncrInfo.NewCrypto(ikeSAKey.SK_ei) + require.NoError(t, err) + + ikeSAKey.SK_er, err = hex.DecodeString( + "3ea57e7ddfb30756" + + "a04619a9873333b0" + + "8e94deef05b6a05d" + + "7eb3dba075d81c6f") + require.NoError(t, err) + ikeSAKey.Encr_r, err = ikeSAKey.EncrInfo.NewCrypto(ikeSAKey.SK_er) + require.NoError(t, err) + + ikeSAKey.SK_ai, err = hex.DecodeString( + "ab8047415535cf53" + + "e19a69e2c86feadf" + + "ebfff1e9") + require.NoError(t, err) + ikeSAKey.Integ_i = ikeSAKey.IntegInfo.Init(ikeSAKey.SK_ai) + + ikeSAKey.SK_ar, err = hex.DecodeString( + "16d5ae6f2859a73a" + + "8c7db60bed07e245" + + "38b19bb0") + require.NoError(t, err) + ikeSAKey.Integ_r = ikeSAKey.IntegInfo.Init(ikeSAKey.SK_ar) + + expIkeMsg := &message.IKEMessage{ + IKEHeader: &message.IKEHeader{ + InitiatorSPI: 0x000000000006f708, + ResponderSPI: 0xc9e2e31f8b64053d, + MajorVersion: 2, + MinorVersion: 0, + ExchangeType: message.IKE_AUTH, + Flags: 0x08, + MessageID: 0x03, + NextPayload: uint8(message.TypeEAP), + }, + } + + expIkePayloads := message.IKEPayloadContainer{ + &message.EAP{ + Code: 0x02, + Identifier: 0x3b, + EAPTypeData: message.EAPTypeDataContainer{ + &message.EAPExpanded{ + VendorID: 0x28af, + VendorType: 0x03, + VendorData: []byte{ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x15, 0x7e, 0x00, + 0x57, 0x2d, 0x10, 0xf5, 0x07, 0x36, 0x2e, 0x32, + 0x2d, 0xe3, 0x68, 0x57, 0x93, 0x65, 0xd2, 0x86, + 0x2b, 0x50, 0xed, + }, + }, + }, + }, + } + + expIkeMsg.Payloads = append(expIkeMsg.Payloads, expIkePayloads...) + + b, err := EncodeEncrypt(expIkeMsg, ikeSAKey, message.Role_Initiator) + require.NoError(t, err) + + ikehdr, err := message.ParseIkeHeader(b) + require.NoError(t, err) + + ikeMsg, err := DecodeDecrypt(b, ikehdr, ikeSAKey, message.Role_Responder) + require.NoError(t, err) + + require.Equal(t, expIkePayloads, ikeMsg.Payloads) +} + +func TestDecodeDecrypt(t *testing.T) { + testcases := []struct { + description string + b []byte + ikeSAKey *security.IKESAKey + sk_ei, sk_er, sk_ai, sk_ar []byte + expErr bool + expIkeMsg *message.IKEMessage + }{ + { + description: "decrypt with key", + b: []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xf7, 0x08, + 0xc9, 0xe2, 0xe3, 0x1f, 0x8b, 0x64, 0x05, 0x3d, + 0x2e, 0x20, 0x23, 0x08, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x6c, 0x30, 0x00, 0x00, 0x50, + 0xec, 0x50, 0x31, 0x16, 0x2c, 0x69, 0x2f, 0xbb, + 0xfc, 0x4d, 0x20, 0x64, 0x0c, 0x91, 0x21, 0xeb, + 0xe9, 0x47, 0x5e, 0xf9, 0x4f, 0x9b, 0x02, 0x95, + 0x9d, 0x31, 0x24, 0x2e, 0x53, 0x5e, 0x9c, 0x3c, + 0x4d, 0xca, 0xec, 0xd1, 0xbf, 0xd6, 0xdd, 0x80, + 0xaa, 0x81, 0x2b, 0x07, 0xde, 0x36, 0xde, 0xe9, + 0xb7, 0x50, 0x94, 0x35, 0xf6, 0x35, 0xe1, 0xaa, + 0xae, 0x1c, 0x38, 0x25, 0xf4, 0xea, 0xe3, 0x38, + 0x49, 0x03, 0xf7, 0x24, 0xf4, 0x44, 0x17, 0x0c, + 0x68, 0x45, 0xca, 0x80, + }, + ikeSAKey: &security.IKESAKey{ + EncrInfo: encr.StrToType("ENCR_AES_CBC_256"), + IntegInfo: integ.StrToType("AUTH_HMAC_SHA1_96"), + }, + sk_ei: []byte{ + 0x3d, 0x7a, 0x26, 0x41, 0x71, 0x22, 0xce, 0xe9, + 0xc7, 0x7c, 0x59, 0xf3, 0x75, 0xb0, 0x24, 0xcd, + 0xb9, 0xf0, 0xb5, 0x77, 0x7e, 0xa1, 0x8b, 0x50, + 0xf8, 0xa6, 0x71, 0xfd, 0x3b, 0x2d, 0xaa, 0x99, + }, + sk_er: []byte{ + 0x3e, 0xa5, 0x7e, 0x7d, 0xdf, 0xb3, 0x07, 0x56, + 0xa0, 0x46, 0x19, 0xa9, 0x87, 0x33, 0x33, 0xb0, + 0x8e, 0x94, 0xde, 0xef, 0x05, 0xb6, 0xa0, 0x5d, + 0x7e, 0xb3, 0xdb, 0xa0, 0x75, 0xd8, 0x1c, 0x6f, + }, + sk_ai: []byte{ + 0xab, 0x80, 0x47, 0x41, 0x55, 0x35, 0xcf, 0x53, + 0xe1, 0x9a, 0x69, 0xe2, 0xc8, 0x6f, 0xea, 0xdf, + 0xeb, 0xff, 0xf1, 0xe9, + }, + sk_ar: []byte{ + 0x16, 0xd5, 0xae, 0x6f, 0x28, 0x59, 0xa7, 0x3a, + 0x8c, 0x7d, 0xb6, 0x0b, 0xed, 0x07, 0xe2, 0x45, + 0x38, 0xb1, 0x9b, 0xb0, + }, + expErr: false, + expIkeMsg: &message.IKEMessage{ + IKEHeader: &message.IKEHeader{ + InitiatorSPI: 0x000000000006f708, + ResponderSPI: 0xc9e2e31f8b64053d, + MajorVersion: 2, + MinorVersion: 0, + ExchangeType: message.IKE_AUTH, + Flags: 0x08, + MessageID: 0x03, + NextPayload: uint8(message.TypeSK), + }, + Payloads: message.IKEPayloadContainer{ + &message.EAP{ + Code: 0x02, + Identifier: 0x3b, + EAPTypeData: []message.EAPTypeFormat{ + &message.EAPExpanded{ + VendorID: 0x28af, + VendorType: 0x03, + VendorData: []byte{ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x15, 0x7e, 0x00, + 0x57, 0x2d, 0x10, 0xf5, 0x07, 0x36, 0x2e, 0x32, + 0x2d, 0xe3, 0x68, 0x57, 0x93, 0x65, 0xd2, 0x86, + 0x2b, 0x50, 0xed, + }, + }, + }, + }, + }, + }, + }, + { + description: "decrypt without key", + b: []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xf7, 0x08, + 0xc9, 0xe2, 0xe3, 0x1f, 0x8b, 0x64, 0x05, 0x3d, + 0x2e, 0x20, 0x23, 0x08, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x6c, 0x30, 0x00, 0x00, 0x50, + 0xec, 0x50, 0x31, 0x16, 0x2c, 0x69, 0x2f, 0xbb, + 0xfc, 0x4d, 0x20, 0x64, 0x0c, 0x91, 0x21, 0xeb, + 0xe9, 0x47, 0x5e, 0xf9, 0x4f, 0x9b, 0x02, 0x95, + 0x9d, 0x31, 0x24, 0x2e, 0x53, 0x5e, 0x9c, 0x3c, + 0x4d, 0xca, 0xec, 0xd1, 0xbf, 0xd6, 0xdd, 0x80, + 0xaa, 0x81, 0x2b, 0x07, 0xde, 0x36, 0xde, 0xe9, + 0xb7, 0x50, 0x94, 0x35, 0xf6, 0x35, 0xe1, 0xaa, + 0xae, 0x1c, 0x38, 0x25, 0xf4, 0xea, 0xe3, 0x38, + 0x49, 0x03, 0xf7, 0x24, 0xf4, 0x44, 0x17, 0x0c, + 0x68, 0x45, 0xca, 0x80, + }, + expErr: true, + }, + { + description: "msg len less than IKE_HEADER_LEN", + b: []byte{}, + expErr: true, + }, + { + description: "no sk_ai", + b: []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xf7, 0x08, + 0xc9, 0xe2, 0xe3, 0x1f, 0x8b, 0x64, 0x05, 0x3d, + 0x2e, 0x20, 0x23, 0x08, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x6c, 0x30, 0x00, 0x00, 0x50, + 0xec, 0x50, 0x31, 0x16, 0x2c, 0x69, 0x2f, 0xbb, + 0xfc, 0x4d, 0x20, 0x64, 0x0c, 0x91, 0x21, 0xeb, + 0xe9, 0x47, 0x5e, 0xf9, 0x4f, 0x9b, 0x02, 0x95, + 0x9d, 0x31, 0x24, 0x2e, 0x53, 0x5e, 0x9c, 0x3c, + 0x4d, 0xca, 0xec, 0xd1, 0xbf, 0xd6, 0xdd, 0x80, + 0xaa, 0x81, 0x2b, 0x07, 0xde, 0x36, 0xde, 0xe9, + 0xb7, 0x50, 0x94, 0x35, 0xf6, 0x35, 0xe1, 0xaa, + 0xae, 0x1c, 0x38, 0x25, 0xf4, 0xea, 0xe3, 0x38, + 0x49, 0x03, 0xf7, 0x24, 0xf4, 0x44, 0x17, 0x0c, + 0x68, 0x45, 0xca, 0x80, + }, + ikeSAKey: &security.IKESAKey{ + EncrInfo: encr.StrToType("ENCR_AES_CBC_256"), + IntegInfo: integ.StrToType("AUTH_HMAC_SHA1_96"), + }, + sk_ei: []byte{ + 0x3d, 0x7a, 0x26, 0x41, 0x71, 0x22, 0xce, 0xe9, + 0xc7, 0x7c, 0x59, 0xf3, 0x75, 0xb0, 0x24, 0xcd, + 0xb9, 0xf0, 0xb5, 0x77, 0x7e, 0xa1, 0x8b, 0x50, + 0xf8, 0xa6, 0x71, 0xfd, 0x3b, 0x2d, 0xaa, 0x99, + }, + sk_er: []byte{ + 0x3e, 0xa5, 0x7e, 0x7d, 0xdf, 0xb3, 0x07, 0x56, + 0xa0, 0x46, 0x19, 0xa9, 0x87, 0x33, 0x33, 0xb0, + 0x8e, 0x94, 0xde, 0xef, 0x05, 0xb6, 0xa0, 0x5d, + 0x7e, 0xb3, 0xdb, 0xa0, 0x75, 0xd8, 0x1c, 0x6f, + }, + sk_ar: []byte{ + 0x16, 0xd5, 0xae, 0x6f, 0x28, 0x59, 0xa7, 0x3a, + 0x8c, 0x7d, 0xb6, 0x0b, 0xed, 0x07, 0xe2, 0x45, + 0x38, 0xb1, 0x9b, 0xb0, + }, + expErr: true, + }, + { + description: "no sk_ei", + b: []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xf7, 0x08, + 0xc9, 0xe2, 0xe3, 0x1f, 0x8b, 0x64, 0x05, 0x3d, + 0x2e, 0x20, 0x23, 0x08, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x6c, 0x30, 0x00, 0x00, 0x50, + 0xec, 0x50, 0x31, 0x16, 0x2c, 0x69, 0x2f, 0xbb, + 0xfc, 0x4d, 0x20, 0x64, 0x0c, 0x91, 0x21, 0xeb, + 0xe9, 0x47, 0x5e, 0xf9, 0x4f, 0x9b, 0x02, 0x95, + 0x9d, 0x31, 0x24, 0x2e, 0x53, 0x5e, 0x9c, 0x3c, + 0x4d, 0xca, 0xec, 0xd1, 0xbf, 0xd6, 0xdd, 0x80, + 0xaa, 0x81, 0x2b, 0x07, 0xde, 0x36, 0xde, 0xe9, + 0xb7, 0x50, 0x94, 0x35, 0xf6, 0x35, 0xe1, 0xaa, + 0xae, 0x1c, 0x38, 0x25, 0xf4, 0xea, 0xe3, 0x38, + 0x49, 0x03, 0xf7, 0x24, 0xf4, 0x44, 0x17, 0x0c, + 0x68, 0x45, 0xca, 0x80, + }, + ikeSAKey: &security.IKESAKey{ + EncrInfo: encr.StrToType("ENCR_AES_CBC_256"), + IntegInfo: integ.StrToType("AUTH_HMAC_SHA1_96"), + }, + sk_er: []byte{ + 0x3e, 0xa5, 0x7e, 0x7d, 0xdf, 0xb3, 0x07, 0x56, + 0xa0, 0x46, 0x19, 0xa9, 0x87, 0x33, 0x33, 0xb0, + 0x8e, 0x94, 0xde, 0xef, 0x05, 0xb6, 0xa0, 0x5d, + 0x7e, 0xb3, 0xdb, 0xa0, 0x75, 0xd8, 0x1c, 0x6f, + }, + sk_ai: []byte{ + 0xab, 0x80, 0x47, 0x41, 0x55, 0x35, 0xcf, 0x53, + 0xe1, 0x9a, 0x69, 0xe2, 0xc8, 0x6f, 0xea, 0xdf, + 0xeb, 0xff, 0xf1, 0xe9, + }, + sk_ar: []byte{ + 0x16, 0xd5, 0xae, 0x6f, 0x28, 0x59, 0xa7, 0x3a, + 0x8c, 0x7d, 0xb6, 0x0b, 0xed, 0x07, 0xe2, 0x45, + 0x38, 0xb1, 0x9b, 0xb0, + }, + expErr: true, + }, + { + description: "invalid checksum", + b: []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xf7, 0x08, + 0xc9, 0xe2, 0xe3, 0x1f, 0x8b, 0x64, 0x05, 0x3d, + 0x2e, 0x20, 0x23, 0x08, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x6c, 0x30, 0x00, 0x00, 0x50, + 0xec, 0x50, 0x31, 0x16, 0x2c, 0x69, 0x2f, 0xbb, + 0xfc, 0x4d, 0x20, 0x64, 0x0c, 0x91, 0x21, 0xeb, + 0xe9, 0x47, 0x5e, 0xf9, 0x4f, 0x9b, 0x02, 0x95, + 0x9d, 0x31, 0x24, 0x2e, 0x53, 0x5e, 0x9c, 0x3c, + 0x4d, 0xca, 0xec, 0xd1, 0xbf, 0xd6, 0xdd, 0x80, + 0xaa, 0x81, 0x2b, 0x07, 0xde, 0x36, 0xde, 0xe9, + 0xb7, 0x50, 0x94, 0x35, 0xf6, 0x35, 0xe1, 0xaa, + 0xae, 0x1c, 0x38, 0x25, 0xf4, 0xea, 0xe3, 0x38, + 0x49, 0x03, 0xf7, 0x24, 0xf4, 0x44, 0x17, 0x0c, + 0x00, 0x00, 0x00, 0x00, + }, + ikeSAKey: &security.IKESAKey{ + EncrInfo: encr.StrToType("ENCR_AES_CBC_256"), + IntegInfo: integ.StrToType("AUTH_HMAC_SHA1_96"), + }, + sk_ei: []byte{ + 0x3d, 0x7a, 0x26, 0x41, 0x71, 0x22, 0xce, 0xe9, + 0xc7, 0x7c, 0x59, 0xf3, 0x75, 0xb0, 0x24, 0xcd, + 0xb9, 0xf0, 0xb5, 0x77, 0x7e, 0xa1, 0x8b, 0x50, + 0xf8, 0xa6, 0x71, 0xfd, 0x3b, 0x2d, 0xaa, 0x99, + }, + sk_er: []byte{ + 0x3e, 0xa5, 0x7e, 0x7d, 0xdf, 0xb3, 0x07, 0x56, + 0xa0, 0x46, 0x19, 0xa9, 0x87, 0x33, 0x33, 0xb0, + 0x8e, 0x94, 0xde, 0xef, 0x05, 0xb6, 0xa0, 0x5d, + 0x7e, 0xb3, 0xdb, 0xa0, 0x75, 0xd8, 0x1c, 0x6f, + }, + sk_ai: []byte{ + 0xab, 0x80, 0x47, 0x41, 0x55, 0x35, 0xcf, 0x53, + 0xe1, 0x9a, 0x69, 0xe2, 0xc8, 0x6f, 0xea, 0xdf, + 0xeb, 0xff, 0xf1, 0xe9, + }, + sk_ar: []byte{ + 0x16, 0xd5, 0xae, 0x6f, 0x28, 0x59, 0xa7, 0x3a, + 0x8c, 0x7d, 0xb6, 0x0b, 0xed, 0x07, 0xe2, 0x45, + 0x38, 0xb1, 0x9b, 0xb0, + }, + expErr: true, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var err error + var ikeMsg *message.IKEMessage + + if len(tc.sk_ai) > 0 { + tc.ikeSAKey.Integ_i = tc.ikeSAKey.IntegInfo.Init(tc.sk_ai) + } + if len(tc.sk_ar) > 0 { + tc.ikeSAKey.Integ_r = tc.ikeSAKey.IntegInfo.Init(tc.sk_ar) + } + + if len(tc.sk_ei) > 0 { + tc.ikeSAKey.Encr_i, err = tc.ikeSAKey.EncrInfo.NewCrypto(tc.sk_ei) + require.NoError(t, err) + } + + if len(tc.sk_er) > 0 { + tc.ikeSAKey.Encr_r, err = tc.ikeSAKey.EncrInfo.NewCrypto(tc.sk_er) + require.NoError(t, err) + } + + ikeMsg, err = DecodeDecrypt(tc.b, nil, tc.ikeSAKey, message.Role_Responder) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expIkeMsg.Payloads, ikeMsg.Payloads) + require.Equal(t, *tc.expIkeMsg.IKEHeader, *ikeMsg.IKEHeader) + } + }) + } +} + +func TestEncryptMsg(t *testing.T) { + encryptionAlgorithm := encr.StrToType("ENCR_AES_CBC_256") + + integrityAlgorithm := integ.StrToType("AUTH_HMAC_SHA1_96") + + ikeSAKey := &security.IKESAKey{ + EncrInfo: encryptionAlgorithm, + IntegInfo: integrityAlgorithm, + } + + var err error + sk_ei, err := hex.DecodeString( + "3d7a26417122cee9c77c59f375b024cdb9f0b5777ea18b50f8a671fd3b2daa99") + require.NoError(t, err) + ikeSAKey.Encr_i, err = ikeSAKey.EncrInfo.NewCrypto(sk_ei) + require.NoError(t, err) + + sk_er, err := hex.DecodeString( + "3ea57e7ddfb30756a04619a9873333b08e94deef05b6a05d7eb3dba075d81c6f") + require.NoError(t, err) + ikeSAKey.Encr_r, err = ikeSAKey.EncrInfo.NewCrypto(sk_er) + require.NoError(t, err) + + sk_ai, err := hex.DecodeString( + "ab8047415535cf53e19a69e2c86feadfebfff1e9") + require.NoError(t, err) + ikeSAKey.Integ_i = ikeSAKey.IntegInfo.Init(sk_ai) + + sk_ar, err := hex.DecodeString( + "16d5ae6f2859a73a8c7db60bed07e24538b19bb0") + require.NoError(t, err) + integ_r := ikeSAKey.IntegInfo.Init(sk_ar) + ikeSAKey.Integ_r = integ_r + + ikeMessage := &message.IKEMessage{ + IKEHeader: &message.IKEHeader{ + ResponderSPI: 0xc9e2e31f8b64053d, + InitiatorSPI: 0x000000000006f708, + MajorVersion: 2, + MinorVersion: 0, + ExchangeType: message.IKE_AUTH, + Flags: 0x08, + MessageID: 0x03, + }, + } + + ikePayloads := message.IKEPayloadContainer{ + &message.EAP{ + Code: 0x02, + Identifier: 0x3b, + EAPTypeData: message.EAPTypeDataContainer{ + &message.EAPExpanded{ + VendorID: 0x28af, + VendorType: 0x03, + VendorData: []byte{ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x15, 0x7e, 0x00, + 0x57, 0x2d, 0x10, 0xf5, 0x07, 0x36, 0x2e, 0x32, + 0x2d, 0xe3, 0x68, 0x57, 0x93, 0x65, 0xd2, 0x86, + 0x2b, 0x50, 0xed, + }, + }, + }, + }, + } + ikeMessage.Payloads = ikePayloads + + // Successful encryption + err = encryptMsg(ikeMessage, ikeSAKey, message.Role_Initiator) + require.NoError(t, err) + + rawMsg, err := ikeMessage.Encode() + require.NoError(t, err) + + ikeMessage, err = decryptMsg( + rawMsg, ikeMessage, ikeSAKey, message.Role_Responder) + require.NoError(t, err) + require.Equal(t, ikePayloads, ikeMessage.Payloads) + + // IKE Security Association is nil + err = encryptMsg(ikeMessage, nil, message.Role_Initiator) + require.Error(t, err) + + // No IKE payload to be encrypted + ikeMessage.Payloads.Reset() + err = encryptMsg(ikeMessage, ikeSAKey, message.Role_Initiator) + require.Error(t, err) + ikeMessage.Payloads = ikePayloads + + // Response IKE Message is nil + err = encryptMsg(nil, ikeSAKey, message.Role_Initiator) + require.Error(t, err) + + // No integrity algorithm specified + ikeSAKey.IntegInfo = nil + err = encryptMsg(ikeMessage, ikeSAKey, message.Role_Initiator) + require.Error(t, err) + + ikeSAKey.IntegInfo = integrityAlgorithm + + // No encryption algorithm specified + ikeSAKey.EncrInfo = nil + err = encryptMsg(ikeMessage, ikeSAKey, message.Role_Initiator) + require.Error(t, err) + + ikeSAKey.EncrInfo = encryptionAlgorithm + + // No responder's integrity key + ikeSAKey.Integ_r = nil + err = encryptMsg(ikeMessage, ikeSAKey, message.Role_Initiator) + require.Error(t, err) + + ikeSAKey.Integ_r = integ_r + + // No responder's encryption key + ikeSAKey.Encr_r = nil + err = encryptMsg(ikeMessage, ikeSAKey, message.Role_Initiator) + require.Error(t, err) +} + +func TestVerifyIntegrity(t *testing.T) { + tests := []struct { + name string + key string + originData []byte + checksum string + ikeSAKey *security.IKESAKey + role message.Role + expectedValid bool + }{ + { + name: "HMAC MD5 96 - valid", + key: "0123456789abcdef0123456789abcdef", + originData: []byte("hello world"), + checksum: "c30f366e411540f68221d04a", + ikeSAKey: &security.IKESAKey{ + IntegInfo: integ.StrToType("AUTH_HMAC_MD5_96"), + }, + role: message.Role_Responder, + expectedValid: true, + }, + { + name: "HMAC MD5 96 - invalid checksum", + key: "0123456789abcdef0123456789abcdef", + originData: []byte("hello world"), + checksum: "01231875aa", + ikeSAKey: &security.IKESAKey{ + IntegInfo: integ.StrToType("AUTH_HMAC_MD5_96"), + }, + role: message.Role_Responder, + expectedValid: false, + }, + { + name: "HMAC MD5 96 - invalid key length", + key: "0123", + originData: []byte("hello world"), + ikeSAKey: &security.IKESAKey{ + IntegInfo: integ.StrToType("AUTH_HMAC_MD5_96"), + }, + role: message.Role_Responder, + expectedValid: false, + }, + { + name: "HMAC SHA1 96 - valid", + key: "0123456789abcdef0123456789abcdef01234567", + originData: []byte("hello world"), + checksum: "5089f6a86e4dafb89e3fcd23", + ikeSAKey: &security.IKESAKey{ + IntegInfo: integ.StrToType("AUTH_HMAC_SHA1_96"), + }, + role: message.Role_Initiator, + expectedValid: true, + }, + { + name: "HMAC SHA1 96 - invalid checksum", + key: "0123456789abcdef0123456789abcdef01234567", + originData: []byte("hello world"), + checksum: "01231875aa", + ikeSAKey: &security.IKESAKey{ + IntegInfo: integ.StrToType("AUTH_HMAC_SHA1_96"), + }, + role: message.Role_Initiator, + expectedValid: false, + }, + { + name: "HMAC SHA1 96 - invalid key length", + key: "0123", + originData: []byte("hello world"), + ikeSAKey: &security.IKESAKey{ + IntegInfo: integ.StrToType("AUTH_HMAC_SHA1_96"), + }, + role: message.Role_Initiator, + expectedValid: false, + }, + { + name: "HMAC SHA256 128 - valid", + key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + originData: []byte("hello world"), + checksum: "a64166565bc1f48eb3edd4109fcaeb72", + ikeSAKey: &security.IKESAKey{ + IntegInfo: integ.StrToType("AUTH_HMAC_SHA2_256_128"), + }, + role: message.Role_Initiator, + expectedValid: true, + }, + { + name: "HMAC SHA256 128 - invalid checksum", + key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + originData: []byte("hello world"), + checksum: "01231875aa", + ikeSAKey: &security.IKESAKey{ + IntegInfo: integ.StrToType("AUTH_HMAC_SHA2_256_128"), + }, + role: message.Role_Initiator, + expectedValid: false, + }, + { + name: "HMAC SHA256 128 - invalid key length", + key: "0123", + originData: []byte("hello world"), + ikeSAKey: &security.IKESAKey{ + IntegInfo: integ.StrToType("AUTH_HMAC_SHA2_256_128"), + }, + role: message.Role_Initiator, + expectedValid: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var key, checksum []byte + var err error + checksum, err = hex.DecodeString(tt.checksum) + require.NoError(t, err, "failed to decode checksum hex string") + + key, err = hex.DecodeString(tt.key) + require.NoError(t, err, "failed to decode key hex string") + + integ := tt.ikeSAKey.IntegInfo.Init(key) + + if tt.role == message.Role_Initiator { + tt.ikeSAKey.Integ_i = integ + } else { + tt.ikeSAKey.Integ_r = integ + } + + err = verifyIntegrity(tt.originData, checksum, tt.ikeSAKey, tt.role) + if tt.expectedValid { + require.NoError(t, err, "verifyIntegrity returned an error") + } + }) + } +} diff --git a/internal/encr/encr.go b/internal/encr/encr.go deleted file mode 100644 index a7bbd80..0000000 --- a/internal/encr/encr.go +++ /dev/null @@ -1,228 +0,0 @@ -package encr - -import ( - "fmt" - - "github.com/sirupsen/logrus" - - "github.com/free5gc/ike/internal/logger" - itypes "github.com/free5gc/ike/internal/types" - "github.com/free5gc/ike/message" - types "github.com/free5gc/ike/types" -) - -var ( - encrLog *logrus.Entry - encrString map[uint16]func(uint16, uint16, []byte) string -) - -var ( - encrTypes map[string]ENCRType - encrKTypes map[string]ENCRKType -) - -func init() { - // Log - encrLog = logger.ENCRLog - - // ENCR String - encrString = make(map[uint16]func(uint16, uint16, []byte) string) - encrString[types.ENCR_AES_CBC] = toString_ENCR_AES_CBC - - // ENCR Types - encrTypes = make(map[string]ENCRType) - - encrTypes[string_ENCR_AES_CBC_128] = &ENCR_AES_CBC{ - keyLength: 16, - } - encrTypes[string_ENCR_AES_CBC_192] = &ENCR_AES_CBC{ - keyLength: 24, - } - encrTypes[string_ENCR_AES_CBC_256] = &ENCR_AES_CBC{ - keyLength: 32, - } - - // Default Priority - priority := []string{ - string_ENCR_AES_CBC_128, - string_ENCR_AES_CBC_192, - string_ENCR_AES_CBC_256, - } - - // Set Priority - for i, s := range priority { - if encrType, ok := encrTypes[s]; ok { - encrType.setPriority(uint32(i)) - } else { - encrLog.Error("No such ENCR implementation") - panic("IKE ENCR failed to init.") - } - } - - // ENCR Kernel Types - encrKTypes = make(map[string]ENCRKType) - - encrKTypes[string_ENCR_AES_CBC_128] = &ENCR_AES_CBC{ - keyLength: 16, - } - encrKTypes[string_ENCR_AES_CBC_192] = &ENCR_AES_CBC{ - keyLength: 24, - } - encrKTypes[string_ENCR_AES_CBC_256] = &ENCR_AES_CBC{ - keyLength: 32, - } - - // ENCR Kernel Priority same as above - // Set Priority - for i, s := range priority { - if encrKType, ok := encrKTypes[s]; ok { - encrKType.setPriority(uint32(i)) - } else { - encrLog.Error("No such ENCR implementation") - panic("IKE ENCR failed to init.") - } - } -} - -func SetPriority(algolist []string) error { - // check implemented - for _, algo := range algolist { - if _, ok := encrTypes[algo]; !ok { - return fmt.Errorf("No such implementation: %s", algo) - } - } - // set priority - for i, algo := range algolist { - encrTypes[algo].setPriority(uint32(i)) - } - return nil -} - -func SetKPriority(algolist map[string]uint32) error { - // check implemented - for algo := range algolist { - if _, ok := encrKTypes[algo]; !ok { - return fmt.Errorf("No such implementation: %s", algo) - } - } - // set priority - for algo, priority := range algolist { - encrKTypes[algo].setPriority(priority) - } - return nil -} - -func StrToType(algo string) ENCRType { - if t, ok := encrTypes[algo]; ok { - return t - } else { - return nil - } -} - -func StrToKType(algo string) ENCRKType { - if t, ok := encrKTypes[algo]; ok { - return t - } else { - return nil - } -} - -func DecodeTransform(transform *message.Transform) ENCRType { - if f, ok := encrString[transform.TransformID]; ok { - s := f(transform.AttributeType, transform.AttributeValue, transform.VariableLengthAttributeValue) - if s != "" { - if encrType, ok := encrTypes[s]; ok { - return encrType - } else { - return nil - } - } else { - return nil - } - } else { - return nil - } -} - -func ToTransform(encrType ENCRType) *message.Transform { - t := new(message.Transform) - t.TransformType = types.TypeEncryptionAlgorithm - t.TransformID = encrType.transformID() - t.AttributePresent, t.AttributeType, t.AttributeValue, t.VariableLengthAttributeValue = encrType.getAttribute() - if t.AttributePresent && t.VariableLengthAttributeValue == nil { - t.AttributeFormat = types.AttributeFormatUseTV - } - return t -} - -func DecodeTransformChildSA(transform *message.Transform) ENCRKType { - if f, ok := encrString[transform.TransformID]; ok { - s := f(transform.AttributeType, transform.AttributeValue, transform.VariableLengthAttributeValue) - if s != "" { - if encrKType, ok := encrKTypes[s]; ok { - return encrKType - } else { - return nil - } - } else { - return nil - } - } else { - return nil - } -} - -func ToTransformChildSA(encrKType ENCRKType) *message.Transform { - t := new(message.Transform) - t.TransformType = types.TypeEncryptionAlgorithm - t.TransformID = encrKType.transformID() - t.AttributePresent, t.AttributeType, t.AttributeValue, t.VariableLengthAttributeValue = encrKType.getAttribute() - if t.AttributePresent && t.VariableLengthAttributeValue == nil { - t.AttributeFormat = 1 // TV - } - return t -} - -type ENCRType interface { - transformID() uint16 - getAttribute() (bool, uint16, uint16, []byte) - setPriority(uint32) - Priority() uint32 - GetKeyLength() int - Init(key []byte) itypes.IKECrypto -} - -type ENCRKType interface { - transformID() uint16 - getAttribute() (bool, uint16, uint16, []byte) - setPriority(uint32) - Priority() uint32 - GetKeyLength() int - XFRMString() string -} - -/* Archive for future use -type XFRMEncryptionAlgorithmType uint16 - -func (xfrmEncryptionAlgorithmType XFRMEncryptionAlgorithmType) String() string { - switch xfrmEncryptionAlgorithmType { - case message.ENCR_DES: - return "cbc(des)" - case message.ENCR_3DES: - return "cbc(des3_ede)" - case message.ENCR_CAST: - return "cbc(cast5)" - case message.ENCR_BLOWFISH: - return "cbc(blowfish)" - case message.ENCR_NULL: - return "ecb(cipher_null)" - case message.ENCR_AES_CBC: - return "cbc(aes)" - case message.ENCR_AES_CTR: - return "rfc3686(ctr(aes))" - default: - return "" - } -} -*/ diff --git a/internal/esn/esn.go b/internal/esn/esn.go deleted file mode 100644 index 8f8cac3..0000000 --- a/internal/esn/esn.go +++ /dev/null @@ -1,107 +0,0 @@ -package esn - -import ( - "errors" - - "github.com/sirupsen/logrus" - - "github.com/free5gc/ike/internal/logger" - "github.com/free5gc/ike/message" - "github.com/free5gc/ike/types" -) - -var ( - esnLog *logrus.Entry - esnString map[uint16]func(uint16, uint16, []byte) string - esnTypes map[string]ESNType -) - -func init() { - // Log - esnLog = logger.ESNLog - - // ESN String - esnString = make(map[uint16]func(uint16, uint16, []byte) string) - esnString[types.ESN_ENABLE] = toString_ESN_ENABLE - esnString[types.ESN_DISABLE] = toString_ESN_DISABLE - - // ESN Types - esnTypes = make(map[string]ESNType) - - esnTypes[string_ESN_ENABLE] = &ESN_ENABLE{} - esnTypes[string_ESN_DISABLE] = &ESN_DISABLE{} - - // Default Priority - priority := []string{ - string_ESN_ENABLE, - string_ESN_DISABLE, - } - - // Set Priority - for i, s := range priority { - if esnType, ok := esnTypes[s]; ok { - esnType.setPriority(uint32(i)) - } else { - esnLog.Error("No such ESN implementation") - panic("IKE ESN failed to init.") - } - } -} - -func SetPriority(algolist map[string]uint32) error { - // check implemented - for algo := range algolist { - if _, ok := esnTypes[algo]; !ok { - return errors.New("No such implementation") - } - } - // set priority - for algo, priority := range algolist { - esnTypes[algo].setPriority(priority) - } - return nil -} - -func StrToType(algo string) ESNType { - if t, ok := esnTypes[algo]; ok { - return t - } else { - return nil - } -} - -func DecodeTransform(transform *message.Transform) ESNType { - if f, ok := esnString[transform.TransformID]; ok { - s := f(transform.AttributeType, transform.AttributeValue, transform.VariableLengthAttributeValue) - if s != "" { - if esnType, ok := esnTypes[s]; ok { - return esnType - } else { - return nil - } - } else { - return nil - } - } else { - return nil - } -} - -func ToTransform(esnType ESNType) *message.Transform { - t := new(message.Transform) - t.TransformType = types.TypeExtendedSequenceNumbers - t.TransformID = esnType.transformID() - t.AttributePresent, t.AttributeType, t.AttributeValue, t.VariableLengthAttributeValue = esnType.getAttribute() - if t.AttributePresent && t.VariableLengthAttributeValue == nil { - t.AttributeFormat = types.AttributeFormatUseTV - } - return t -} - -type ESNType interface { - transformID() uint16 - getAttribute() (bool, uint16, uint16, []byte) - setPriority(uint32) - Priority() uint32 - Init() bool -} diff --git a/internal/esn/esn_disable.go b/internal/esn/esn_disable.go deleted file mode 100644 index f301e80..0000000 --- a/internal/esn/esn_disable.go +++ /dev/null @@ -1,37 +0,0 @@ -package esn - -import ( - "github.com/free5gc/ike/types" -) - -const string_ESN_DISABLE string = "ESN_DISABLE" - -func toString_ESN_DISABLE(attrType uint16, intValue uint16, bytesValue []byte) string { - return string_ESN_DISABLE -} - -var _ ESNType = &ESN_DISABLE{} - -type ESN_DISABLE struct { - priority uint32 -} - -func (t *ESN_DISABLE) transformID() uint16 { - return types.ESN_DISABLE -} - -func (t *ESN_DISABLE) getAttribute() (bool, uint16, uint16, []byte) { - return false, 0, 0, nil -} - -func (t *ESN_DISABLE) setPriority(priority uint32) { - t.priority = priority -} - -func (t *ESN_DISABLE) Priority() uint32 { - return t.priority -} - -func (t *ESN_DISABLE) Init() bool { - return false -} diff --git a/internal/esn/esn_enable.go b/internal/esn/esn_enable.go deleted file mode 100644 index c148476..0000000 --- a/internal/esn/esn_enable.go +++ /dev/null @@ -1,37 +0,0 @@ -package esn - -import ( - "github.com/free5gc/ike/types" -) - -const string_ESN_ENABLE string = "ESN_ENABLE" - -func toString_ESN_ENABLE(attrType uint16, intValue uint16, bytesValue []byte) string { - return string_ESN_ENABLE -} - -var _ ESNType = &ESN_ENABLE{} - -type ESN_ENABLE struct { - priority uint32 -} - -func (t *ESN_ENABLE) transformID() uint16 { - return types.ESN_ENABLE -} - -func (t *ESN_ENABLE) getAttribute() (bool, uint16, uint16, []byte) { - return false, 0, 0, nil -} - -func (t *ESN_ENABLE) setPriority(priority uint32) { - t.priority = priority -} - -func (t *ESN_ENABLE) Priority() uint32 { - return t.priority -} - -func (t *ESN_ENABLE) Init() bool { - return true -} diff --git a/internal/logger/logger.go b/internal/logger/logger.go deleted file mode 100644 index 9455884..0000000 --- a/internal/logger/logger.go +++ /dev/null @@ -1,63 +0,0 @@ -package logger - -import ( - "os" - "time" - - formatter "github.com/antonfisher/nested-logrus-formatter" - "github.com/sirupsen/logrus" - - "github.com/free5gc/logger_conf" - "github.com/free5gc/logger_util" -) - -var log *logrus.Logger - -var ( - MsgLog *logrus.Entry - SecLog *logrus.Entry - DHLog *logrus.Entry - ENCRLog *logrus.Entry - ESNLog *logrus.Entry - INTEGLog *logrus.Entry - PRFLog *logrus.Entry -) - -func init() { - log = logrus.New() - log.SetReportCaller(false) - - log.Formatter = &formatter.Formatter{ - TimestampFormat: time.RFC3339, - TrimMessages: true, - NoFieldsSpace: true, - HideKeys: true, - FieldsOrder: []string{"component", "category"}, - } - - free5gcLogHook, err := logger_util.NewFileHook(logger_conf.Free5gcLogFile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0o666) - if err == nil { - log.Hooks.Add(free5gcLogHook) - } - - selfLogHook, err := logger_util.NewFileHook(logger_conf.NfLogDir+"n3iwf.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0o666) - if err == nil { - log.Hooks.Add(selfLogHook) - } - - MsgLog = log.WithFields(logrus.Fields{"component": "IKE", "category": "Message"}) - SecLog = log.WithFields(logrus.Fields{"component": "IKE", "category": "Security"}) - DHLog = log.WithFields(logrus.Fields{"component": "IKE", "category": "DH"}) - ENCRLog = log.WithFields(logrus.Fields{"component": "IKE", "category": "ENCR"}) - ESNLog = log.WithFields(logrus.Fields{"component": "IKE", "category": "ESN"}) - INTEGLog = log.WithFields(logrus.Fields{"component": "IKE", "category": "INTEG"}) - ESNLog = log.WithFields(logrus.Fields{"component": "IKE", "category": "ESN"}) -} - -func SetLogLevel(level logrus.Level) { - log.SetLevel(level) -} - -func SetReportCaller(set bool) { - log.SetReportCaller(set) -} diff --git a/message/build.go b/message/build.go index 2545537..1a73f98 100644 --- a/message/build.go +++ b/message/build.go @@ -4,7 +4,7 @@ import ( "encoding/binary" "net" - "github.com/free5gc/ike/types" + "github.com/pkg/errors" ) func (ikeMessage *IKEMessage) BuildIKEHeader( @@ -14,9 +14,11 @@ func (ikeMessage *IKEMessage) BuildIKEHeader( flags uint8, messageID uint32, ) { + ikeMessage.IKEHeader = new(IKEHeader) ikeMessage.InitiatorSPI = initiatorSPI ikeMessage.ResponderSPI = responsorSPI - ikeMessage.Version = 0x20 + ikeMessage.MajorVersion = 2 + ikeMessage.MinorVersion = 0 ikeMessage.ExchangeType = exchangeType ikeMessage.Flags = flags ikeMessage.MessageID = messageID @@ -47,7 +49,7 @@ func (container *IKEPayloadContainer) BuildCertificate(certificateEncode uint8, *container = append(*container, certificate) } -func (container *IKEPayloadContainer) BuildEncrypted(nextPayload types.IKEPayloadType, +func (container *IKEPayloadContainer) BuildEncrypted(nextPayload IKEPayloadType, encryptedData []byte, ) *Encrypted { encrypted := new(Encrypted) @@ -165,6 +167,17 @@ func (container *ProposalContainer) BuildProposal(proposalNumber uint8, protocol return proposal } +func (container *IKEPayloadContainer) BuildDeletePayload( + protocolID uint8, spiSize uint8, numberOfSPI uint16, spis []byte, +) { + deletePayload := new(Delete) + deletePayload.ProtocolID = protocolID + deletePayload.SPISize = spiSize + deletePayload.NumberOfSPI = numberOfSPI + deletePayload.SPIs = spis + *container = append(*container, deletePayload) +} + func (container *TransformContainer) Reset() { *container = nil } @@ -183,10 +196,10 @@ func (container *TransformContainer) BuildTransform( transform.AttributePresent = true transform.AttributeType = *attributeType if attributeValue != nil { - transform.AttributeFormat = types.AttributeFormatUseTV + transform.AttributeFormat = AttributeFormatUseTV transform.AttributeValue = *attributeValue } else if len(variableLengthAttributeValue) != 0 { - transform.AttributeFormat = types.AttributeFormatUseTLV + transform.AttributeFormat = AttributeFormatUseTLV transform.VariableLengthAttributeValue = append( transform.VariableLengthAttributeValue, variableLengthAttributeValue...) @@ -209,14 +222,14 @@ func (container *IKEPayloadContainer) BuildEAP(code uint8, identifier uint8) *EA func (container *IKEPayloadContainer) BuildEAPSuccess(identifier uint8) { eap := new(EAP) - eap.Code = types.EAPCodeSuccess + eap.Code = EAPCodeSuccess eap.Identifier = identifier *container = append(*container, eap) } func (container *IKEPayloadContainer) BuildEAPfailure(identifier uint8) { eap := new(EAP) - eap.Code = types.EAPCodeFailure + eap.Code = EAPCodeFailure eap.Identifier = identifier *container = append(*container, eap) } @@ -230,27 +243,32 @@ func (container *EAPTypeDataContainer) BuildEAPExpanded(vendorID uint32, vendorT } func (container *IKEPayloadContainer) BuildEAP5GStart(identifier uint8) { - eap := container.BuildEAP(types.EAPCodeRequest, identifier) - eap.EAPTypeData.BuildEAPExpanded(types.VendorID3GPP, types.VendorTypeEAP5G, - []byte{types.EAP5GType5GStart, types.EAP5GSpareValue}) + eap := container.BuildEAP(EAPCodeRequest, identifier) + eap.EAPTypeData.BuildEAPExpanded(VendorID3GPP, VendorTypeEAP5G, + []byte{EAP5GType5GStart, EAP5GSpareValue}) } -func (container *IKEPayloadContainer) BuildEAP5GNAS(identifier uint8, nasPDU []byte) { +func (container *IKEPayloadContainer) BuildEAP5GNAS(identifier uint8, nasPDU []byte) error { if len(nasPDU) == 0 { - msgLog.Error("BuildEAP5GNAS(): NASPDU is nil") - return + return errors.Errorf("BuildEAP5GNAS(): NASPDU is nil") } - + var vendorData []byte header := make([]byte, 4) // Message ID - header[0] = types.EAP5GType5GNAS + header[0] = EAP5GType5GNAS // NASPDU length (2 octets) - binary.BigEndian.PutUint16(header[2:4], uint16(len(nasPDU))) - vendorData := append(header, nasPDU...) + nasPDULen := len(nasPDU) + if nasPDULen > 0xFFFF { + return errors.Errorf("BuildEAP5GNAS(): nasPDU length exceeds uint16 limit: %d", nasPDULen) + } + binary.BigEndian.PutUint16(header[2:4], uint16(nasPDULen)) + vendorData = append(vendorData, header...) + vendorData = append(vendorData, nasPDU...) - eap := container.BuildEAP(types.EAPCodeRequest, identifier) - eap.EAPTypeData.BuildEAPExpanded(types.VendorID3GPP, types.VendorTypeEAP5G, vendorData) + eap := container.BuildEAP(EAPCodeRequest, identifier) + eap.EAPTypeData.BuildEAPExpanded(VendorID3GPP, VendorTypeEAP5G, vendorData) + return nil } func (container *IKEPayloadContainer) BuildNotify5G_QOS_INFO( @@ -258,33 +276,42 @@ func (container *IKEPayloadContainer) BuildNotify5G_QOS_INFO( qfiList []uint8, isDefault bool, isDSCPSpecified bool, - DSCP uint8, -) { + dscp uint8, +) error { notifyData := make([]byte, 1) // For length // Append PDU session ID notifyData = append(notifyData, pduSessionID) // Append QFI list length - notifyData = append(notifyData, uint8(len(qfiList))) + qfiListLen := len(qfiList) + if qfiListLen > 0xFF { + return errors.Errorf("BuildNotify5G_QOS_INFO(): qfiList is too long") + } + notifyData = append(notifyData, uint8(qfiListLen)) // Append QFI list notifyData = append(notifyData, qfiList...) // Append default and differentiated service flags var defaultAndDifferentiatedServiceFlags uint8 if isDefault { - defaultAndDifferentiatedServiceFlags |= types.NotifyType5G_QOS_INFOBitDCSICheck + defaultAndDifferentiatedServiceFlags |= NotifyType5G_QOS_INFOBitDCSICheck } if isDSCPSpecified { - defaultAndDifferentiatedServiceFlags |= types.NotifyType5G_QOS_INFOBitDSCPICheck + defaultAndDifferentiatedServiceFlags |= NotifyType5G_QOS_INFOBitDSCPICheck } notifyData = append(notifyData, defaultAndDifferentiatedServiceFlags) if isDSCPSpecified { - notifyData = append(notifyData, DSCP) + notifyData = append(notifyData, dscp) } // Assign length - notifyData[0] = uint8(len(notifyData)) + notifyDataLen := len(notifyData) + if notifyDataLen > 0xFF { + return errors.Errorf("BuildNotify5G_QOS_INFO(): notifyData is too long") + } + notifyData[0] = uint8(notifyDataLen) - container.BuildNotification(types.TypeNone, types.Vendor3GPPNotifyType5G_QOS_INFO, nil, notifyData) + container.BuildNotification(TypeNone, Vendor3GPPNotifyType5G_QOS_INFO, nil, notifyData) + return nil } func (container *IKEPayloadContainer) BuildNotifyNAS_IP4_ADDRESS(nasIPAddr string) { @@ -292,7 +319,7 @@ func (container *IKEPayloadContainer) BuildNotifyNAS_IP4_ADDRESS(nasIPAddr strin return } else { ipAddrByte := net.ParseIP(nasIPAddr).To4() - container.BuildNotification(types.TypeNone, types.Vendor3GPPNotifyTypeNAS_IP4_ADDRESS, nil, ipAddrByte) + container.BuildNotification(TypeNone, Vendor3GPPNotifyTypeNAS_IP4_ADDRESS, nil, ipAddrByte) } } @@ -301,7 +328,7 @@ func (container *IKEPayloadContainer) BuildNotifyUP_IP4_ADDRESS(upIPAddr string) return } else { ipAddrByte := net.ParseIP(upIPAddr).To4() - container.BuildNotification(types.TypeNone, types.Vendor3GPPNotifyTypeUP_IP4_ADDRESS, nil, ipAddrByte) + container.BuildNotification(TypeNone, Vendor3GPPNotifyTypeUP_IP4_ADDRESS, nil, ipAddrByte) } } @@ -311,6 +338,6 @@ func (container *IKEPayloadContainer) BuildNotifyNAS_TCP_PORT(port uint16) { } else { portData := make([]byte, 2) binary.BigEndian.PutUint16(portData, port) - container.BuildNotification(types.TypeNone, types.Vendor3GPPNotifyTypeNAS_TCP_PORT, nil, portData) + container.BuildNotification(TypeNone, Vendor3GPPNotifyTypeNAS_TCP_PORT, nil, portData) } } diff --git a/message/message.go b/message/message.go index 1ac5802..95b55af 100644 --- a/message/message.go +++ b/message/message.go @@ -2,41 +2,68 @@ package message import ( "encoding/binary" - "encoding/hex" - "errors" - "fmt" - "github.com/sirupsen/logrus" - - "github.com/free5gc/ike/internal/logger" - "github.com/free5gc/ike/types" + "github.com/pkg/errors" ) -// Log -var msgLog *logrus.Entry - -func init() { - msgLog = logger.MsgLog -} +const IKE_HEADER_LEN int = 28 -type IKEMessage struct { +type IKEHeader struct { InitiatorSPI uint64 ResponderSPI uint64 - Version uint8 + MajorVersion uint8 + MinorVersion uint8 ExchangeType uint8 Flags uint8 MessageID uint32 - Payloads IKEPayloadContainer + NextPayload uint8 } -func (ikeMessage *IKEMessage) Encode() ([]byte, error) { - msgLog.Info("Encoding IKE message") +type IKEMessage struct { + *IKEHeader + Payloads IKEPayloadContainer +} + +func ParseIkeHeader(b []byte) (*IKEHeader, error) { + // IKE message packet format this implementation referenced is + // defined in RFC 7296, Section 3.1 + // bounds checking + if len(b) < IKE_HEADER_LEN { + return nil, errors.Errorf("ParseIkeHeader(): Received broken IKE header") + } + ikeMessageLength := binary.BigEndian.Uint32(b[24:IKE_HEADER_LEN]) + if ikeMessageLength < uint32(IKE_HEADER_LEN) { + return nil, errors.Errorf("ParseIkeHeader(): Illegal IKE message length %d < header length %d", + ikeMessageLength, IKE_HEADER_LEN) + } + // len() return int, which is 64 bit on 64-bit host and 32 bit + // on 32-bit host, so this implementation may potentially cause + // problem on 32-bit machine + if len(b) != int(ikeMessageLength) { + return nil, errors.Errorf("ParseIkeHeader(): The length of received message " + + "not matchs the length specified in header") + } + + ikeHeader := new(IKEHeader) + + ikeHeader.InitiatorSPI = binary.BigEndian.Uint64(b[:8]) + ikeHeader.ResponderSPI = binary.BigEndian.Uint64(b[8:16]) + ikeHeader.MajorVersion = b[17] >> 4 + ikeHeader.MinorVersion = b[17] & 0x0F + ikeHeader.ExchangeType = b[18] + ikeHeader.Flags = b[19] + ikeHeader.MessageID = binary.BigEndian.Uint32(b[20:24]) + ikeHeader.NextPayload = b[16] + + return ikeHeader, nil +} - ikeMessageData := make([]byte, 28) +func (ikeMessage *IKEMessage) Encode() ([]byte, error) { + ikeMessageData := make([]byte, IKE_HEADER_LEN) binary.BigEndian.PutUint64(ikeMessageData[0:8], ikeMessage.InitiatorSPI) binary.BigEndian.PutUint64(ikeMessageData[8:16], ikeMessage.ResponderSPI) - ikeMessageData[17] = ikeMessage.Version + ikeMessageData[17] = (ikeMessage.MajorVersion << 4) | (ikeMessage.MinorVersion & 0x0F) ikeMessageData[18] = ikeMessage.ExchangeType ikeMessageData[19] = ikeMessage.Flags binary.BigEndian.PutUint32(ikeMessageData[20:24], ikeMessage.MessageID) @@ -44,56 +71,42 @@ func (ikeMessage *IKEMessage) Encode() ([]byte, error) { if len(ikeMessage.Payloads) > 0 { ikeMessageData[16] = byte(ikeMessage.Payloads[0].Type()) } else { - ikeMessageData[16] = types.NoNext + ikeMessageData[16] = byte(NoNext) } ikeMessagePayloadData, err := ikeMessage.Payloads.Encode() if err != nil { - return nil, fmt.Errorf("Encode(): EncodePayload failed: %+v", err) + return nil, errors.Errorf("Encode(): EncodePayload failed: %+v", err) } ikeMessageData = append(ikeMessageData, ikeMessagePayloadData...) - binary.BigEndian.PutUint32(ikeMessageData[24:28], uint32(len(ikeMessageData))) - - msgLog.Tracef("Encoded %d bytes", len(ikeMessageData)) - msgLog.Tracef("IKE message data:\n%s", hex.Dump(ikeMessageData)) - + ikeMsgDataLen := len(ikeMessageData) + if ikeMsgDataLen > 0xFFFFFFFF { + return nil, errors.Errorf("Encode(): ikeMessageData length exceeds uint32 limit: %d", ikeMsgDataLen) + } + binary.BigEndian.PutUint32(ikeMessageData[24:IKE_HEADER_LEN], uint32(ikeMsgDataLen)) return ikeMessageData, nil } -func (ikeMessage *IKEMessage) Decode(rawData []byte) error { - // IKE message packet format this implementation referenced is - // defined in RFC 7296, Section 3.1 - msgLog.Info("Decoding IKE message") - msgLog.Tracef("Received IKE message:\n%s", hex.Dump(rawData)) - - // bounds checking - if len(rawData) < 28 { - return errors.New("Decode(): Received broken IKE header") - } - ikeMessageLength := binary.BigEndian.Uint32(rawData[24:28]) - if ikeMessageLength < 28 { - return fmt.Errorf("Decode(): Illegal IKE message length %d < header length 20", ikeMessageLength) - } - // len() return int, which is 64 bit on 64-bit host and 32 bit - // on 32-bit host, so this implementation may potentially cause - // problem on 32-bit machine - if len(rawData) != int(ikeMessageLength) { - return errors.New("Decode(): The length of received message not matchs the length specified in header") +func (ikeMessage *IKEMessage) Decode(b []byte) error { + var err error + ikeMessage.IKEHeader, err = ParseIkeHeader(b) + if err != nil { + return errors.Wrapf(err, "Decode()") } - nextPayload := rawData[16] + err = ikeMessage.DecodePayload(b[IKE_HEADER_LEN:]) + if err != nil { + return errors.Errorf("Decode(): DecodePayload failed: %+v", err) + } - ikeMessage.InitiatorSPI = binary.BigEndian.Uint64(rawData[:8]) - ikeMessage.ResponderSPI = binary.BigEndian.Uint64(rawData[8:16]) - ikeMessage.Version = rawData[17] - ikeMessage.ExchangeType = rawData[18] - ikeMessage.Flags = rawData[19] - ikeMessage.MessageID = binary.BigEndian.Uint32(rawData[20:24]) + return nil +} - err := ikeMessage.Payloads.Decode(nextPayload, rawData[28:]) +func (ikeMessage *IKEMessage) DecodePayload(b []byte) error { + err := ikeMessage.Payloads.Decode(ikeMessage.NextPayload, b) if err != nil { - return fmt.Errorf("Decode(): DecodePayload failed: %+v", err) + return errors.Errorf("DecodePayload(): DecodePayload failed: %+v", err) } return nil @@ -102,8 +115,6 @@ func (ikeMessage *IKEMessage) Decode(rawData []byte) error { type IKEPayloadContainer []IKEPayload func (container *IKEPayloadContainer) Encode() ([]byte, error) { - msgLog.Info("Encoding IKE payloads") - ikeMessagePayloadData := make([]byte, 0) for index, payload := range *container { @@ -111,20 +122,24 @@ func (container *IKEPayloadContainer) Encode() ([]byte, error) { if (index + 1) < len(*container) { // if it has next payload payloadData[0] = uint8((*container)[index+1].Type()) } else { - if payload.Type() == types.TypeSK { + if payload.Type() == TypeSK { payloadData[0] = payload.(*Encrypted).NextPayload } else { - payloadData[0] = types.NoNext + payloadData[0] = byte(NoNext) } } data, err := payload.marshal() if err != nil { - return nil, fmt.Errorf("EncodePayload(): Failed to marshal payload: %+v", err) + return nil, errors.Errorf("EncodePayload(): Failed to marshal payload: %+v", err) } payloadData = append(payloadData, data...) - binary.BigEndian.PutUint16(payloadData[2:4], uint16(len(payloadData))) + payloadDataLen := len(payloadData) + if payloadDataLen > 0xFFFF { + return nil, errors.Errorf("EncodePayload(): payloadData length exceeds uint16 limit: %d", payloadDataLen) + } + binary.BigEndian.PutUint16(payloadData[2:4], uint16(payloadDataLen)) ikeMessagePayloadData = append(ikeMessagePayloadData, payloadData...) } @@ -132,82 +147,80 @@ func (container *IKEPayloadContainer) Encode() ([]byte, error) { return ikeMessagePayloadData, nil } -func (container *IKEPayloadContainer) Decode(nextPayload uint8, rawData []byte) error { - msgLog.Info("Decoding IKE payloads") - - for len(rawData) > 0 { +func (container *IKEPayloadContainer) Decode(nextPayload uint8, b []byte) error { + for len(b) > 0 { // bounds checking - msgLog.Trace("DecodePayload(): Decode 1 payload") - if len(rawData) < 4 { - return errors.New("DecodePayload(): No sufficient bytes to decode next payload") + if len(b) < 4 { + return errors.Errorf("DecodePayload(): No sufficient bytes to decode next payload") } - payloadLength := binary.BigEndian.Uint16(rawData[2:4]) + payloadLength := binary.BigEndian.Uint16(b[2:4]) if payloadLength < 4 { - return fmt.Errorf("DecodePayload(): Illegal payload length %d < header length 4", payloadLength) + return errors.Errorf("DecodePayload(): Illegal payload length %d < header length 4", payloadLength) } - if len(rawData) < int(payloadLength) { - return errors.New("DecodePayload(): The length of received message not matchs the length specified in header") + if len(b) < int(payloadLength) { + return errors.Errorf("DecodePayload(): The length of received message not matchs"+ + " the length specified in header: %v", len(b)) } - criticalBit := (rawData[1] & 0x80) >> 7 + criticalBit := (b[1] & 0x80) >> 7 var payload IKEPayload - switch nextPayload { - case types.TypeSA: + switch IKEPayloadType(nextPayload) { + case TypeSA: payload = new(SecurityAssociation) - case types.TypeKE: + case TypeKE: payload = new(KeyExchange) - case types.TypeIDi: + case TypeIDi: payload = new(IdentificationInitiator) - case types.TypeIDr: + case TypeIDr: payload = new(IdentificationResponder) - case types.TypeCERT: + case TypeCERT: payload = new(Certificate) - case types.TypeCERTreq: + case TypeCERTreq: payload = new(CertificateRequest) - case types.TypeAUTH: + case TypeAUTH: payload = new(Authentication) - case types.TypeNiNr: + case TypeNiNr: payload = new(Nonce) - case types.TypeN: + case TypeN: payload = new(Notification) - case types.TypeD: + case TypeD: payload = new(Delete) - case types.TypeV: + case TypeV: payload = new(VendorID) - case types.TypeTSi: + case TypeTSi: payload = new(TrafficSelectorInitiator) - case types.TypeTSr: + case TypeTSr: payload = new(TrafficSelectorResponder) - case types.TypeSK: + case TypeSK: encryptedPayload := new(Encrypted) - encryptedPayload.NextPayload = rawData[0] + encryptedPayload.NextPayload = b[0] payload = encryptedPayload - case types.TypeCP: + case TypeCP: payload = new(Configuration) - case types.TypeEAP: + case TypeEAP: payload = new(EAP) default: if criticalBit == 0 { // Skip this payload - nextPayload = rawData[0] - rawData = rawData[payloadLength:] + nextPayload = b[0] + b = b[payloadLength:] continue } else { // TODO: Reject this IKE message - return fmt.Errorf("Unknown payload type: %d", nextPayload) + return errors.Errorf("Unknown payload type: %d", nextPayload) } } - if err := payload.unmarshal(rawData[4:payloadLength]); err != nil { - return fmt.Errorf("DecodePayload(): Unmarshal payload failed: %+v", err) + if err := payload.unmarshal(b[4:payloadLength]); err != nil { + return errors.Errorf("DecodePayload(): Unmarshal payload failed: %+v", err) } *container = append(*container, payload) - nextPayload = rawData[0] - rawData = rawData[payloadLength:] + nextPayload = b[0] + b = b[payloadLength:] } return nil @@ -215,1315 +228,9 @@ func (container *IKEPayloadContainer) Decode(nextPayload uint8, rawData []byte) type IKEPayload interface { // Type specifies the IKE payload types - Type() types.IKEPayloadType + Type() IKEPayloadType // Called by Encode() or Decode() marshal() ([]byte, error) - unmarshal(rawData []byte) error -} - -// Definition of Security Association - -var _ IKEPayload = &SecurityAssociation{} - -type SecurityAssociation struct { - Proposals ProposalContainer -} - -type ProposalContainer []*Proposal - -type Proposal struct { - ProposalNumber uint8 - ProtocolID uint8 - SPI []byte - EncryptionAlgorithm TransformContainer - PseudorandomFunction TransformContainer - IntegrityAlgorithm TransformContainer - DiffieHellmanGroup TransformContainer - ExtendedSequenceNumbers TransformContainer -} - -type TransformContainer []*Transform - -type Transform struct { - TransformType uint8 - TransformID uint16 - AttributePresent bool - AttributeFormat uint8 - AttributeType uint16 - AttributeValue uint16 - VariableLengthAttributeValue []byte -} - -func (securityAssociation *SecurityAssociation) Type() types.IKEPayloadType { return types.TypeSA } - -func (securityAssociation *SecurityAssociation) marshal() ([]byte, error) { - msgLog.Info("[SecurityAssociation] marshal(): Start marshalling") - - securityAssociationData := make([]byte, 0) - - for proposalIndex, proposal := range securityAssociation.Proposals { - proposalData := make([]byte, 8) - - if (proposalIndex + 1) < len(securityAssociation.Proposals) { - proposalData[0] = 2 - } else { - proposalData[0] = 0 - } - - proposalData[4] = proposal.ProposalNumber - proposalData[5] = proposal.ProtocolID - - proposalData[6] = uint8(len(proposal.SPI)) - if len(proposal.SPI) > 0 { - proposalData = append(proposalData, proposal.SPI...) - } - - // combine all transforms - var transformList []*Transform - transformList = append(transformList, proposal.EncryptionAlgorithm...) - transformList = append(transformList, proposal.PseudorandomFunction...) - transformList = append(transformList, proposal.IntegrityAlgorithm...) - transformList = append(transformList, proposal.DiffieHellmanGroup...) - transformList = append(transformList, proposal.ExtendedSequenceNumbers...) - - if len(transformList) == 0 { - return nil, errors.New("One proposal has no any transform") - } - proposalData[7] = uint8(len(transformList)) - - proposalTransformData := make([]byte, 0) - - for transformIndex, transform := range transformList { - transformData := make([]byte, 8) - - if (transformIndex + 1) < len(transformList) { - transformData[0] = 3 - } else { - transformData[0] = 0 - } - - transformData[4] = transform.TransformType - binary.BigEndian.PutUint16(transformData[6:8], transform.TransformID) - - if transform.AttributePresent { - attributeData := make([]byte, 4) - - if transform.AttributeFormat == 0 { - // TLV - if len(transform.VariableLengthAttributeValue) == 0 { - return nil, errors.New("Attribute of one transform not specified") - } - attributeFormatAndType := ((uint16(transform.AttributeFormat) & 0x1) << 15) | transform.AttributeType - binary.BigEndian.PutUint16(attributeData[0:2], attributeFormatAndType) - binary.BigEndian.PutUint16(attributeData[2:4], uint16(len(transform.VariableLengthAttributeValue))) - attributeData = append(attributeData, transform.VariableLengthAttributeValue...) - } else { - // TV - attributeFormatAndType := ((uint16(transform.AttributeFormat) & 0x1) << 15) | transform.AttributeType - binary.BigEndian.PutUint16(attributeData[0:2], attributeFormatAndType) - binary.BigEndian.PutUint16(attributeData[2:4], transform.AttributeValue) - } - - transformData = append(transformData, attributeData...) - } - - binary.BigEndian.PutUint16(transformData[2:4], uint16(len(transformData))) - - proposalTransformData = append(proposalTransformData, transformData...) - } - - proposalData = append(proposalData, proposalTransformData...) - binary.BigEndian.PutUint16(proposalData[2:4], uint16(len(proposalData))) - - securityAssociationData = append(securityAssociationData, proposalData...) - } - - return securityAssociationData, nil -} - -func (securityAssociation *SecurityAssociation) unmarshal(rawData []byte) error { - msgLog.Info("[SecurityAssociation] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[SecurityAssociation] unmarshal(): Payload length %d bytes", len(rawData)) - - for len(rawData) > 0 { - msgLog.Trace("[SecurityAssociation] unmarshal(): Unmarshal 1 proposal") - // bounds checking - if len(rawData) < 8 { - return errors.New("Proposal: No sufficient bytes to decode next proposal") - } - proposalLength := binary.BigEndian.Uint16(rawData[2:4]) - if proposalLength < 8 { - return errors.New("Proposal: Illegal payload length %d < header length 8") - } - if len(rawData) < int(proposalLength) { - return errors.New("Proposal: The length of received message not matchs the length specified in header") - } - - // Log whether this proposal is the last - if rawData[0] == 0 { - msgLog.Trace("[SecurityAssociation] This proposal is the last") - } - // Log the number of transform in the proposal - msgLog.Tracef("[SecurityAssociation] This proposal contained %d transform", rawData[7]) - - proposal := new(Proposal) - var transformData []byte - - proposal.ProposalNumber = rawData[4] - proposal.ProtocolID = rawData[5] - - spiSize := rawData[6] - if spiSize > 0 { - // bounds checking - if len(rawData) < int(8+spiSize) { - return errors.New("Proposal: No sufficient bytes for unmarshalling SPI of proposal") - } - proposal.SPI = append(proposal.SPI, rawData[8:8+spiSize]...) - } - - transformData = rawData[8+spiSize : proposalLength] - - for len(transformData) > 0 { - // bounds checking - msgLog.Trace("[SecurityAssociation] unmarshal(): Unmarshal 1 transform") - if len(transformData) < 8 { - return errors.New("Transform: No sufficient bytes to decode next transform") - } - transformLength := binary.BigEndian.Uint16(transformData[2:4]) - if transformLength < 8 { - return errors.New("Transform: Illegal payload length %d < header length 8") - } - if len(transformData) < int(transformLength) { - return errors.New("Transform: The length of received message not matchs the length specified in header") - } - - // Log whether this transform is the last - if transformData[0] == 0 { - msgLog.Trace("[SecurityAssociation] This transform is the last") - } - - transform := new(Transform) - - transform.TransformType = transformData[4] - transform.TransformID = binary.BigEndian.Uint16(transformData[6:8]) - if transformLength > 8 { - transform.AttributePresent = true - transform.AttributeFormat = ((transformData[8] & 0x80) >> 7) - transform.AttributeType = binary.BigEndian.Uint16(transformData[8:10]) & 0x7f - - if transform.AttributeFormat == 0 { - attributeLength := binary.BigEndian.Uint16(transformData[10:12]) - // bounds checking - if (12 + attributeLength) != transformLength { - return fmt.Errorf("Illegal attribute length %d not satisfies the transform length %d", - attributeLength, transformLength) - } - copy(transform.VariableLengthAttributeValue, transformData[12:12+attributeLength]) - } else { - transform.AttributeValue = binary.BigEndian.Uint16(transformData[10:12]) - } - } - - switch transform.TransformType { - case types.TypeEncryptionAlgorithm: - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, transform) - case types.TypePseudorandomFunction: - proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, transform) - case types.TypeIntegrityAlgorithm: - proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, transform) - case types.TypeDiffieHellmanGroup: - proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, transform) - case types.TypeExtendedSequenceNumbers: - proposal.ExtendedSequenceNumbers = append(proposal.ExtendedSequenceNumbers, transform) - } - - transformData = transformData[transformLength:] - } - - securityAssociation.Proposals = append(securityAssociation.Proposals, proposal) - - rawData = rawData[proposalLength:] - } - - return nil -} - -// Definition of Key Exchange - -var _ IKEPayload = &KeyExchange{} - -type KeyExchange struct { - DiffieHellmanGroup uint16 - KeyExchangeData []byte -} - -func (keyExchange *KeyExchange) Type() types.IKEPayloadType { return types.TypeKE } - -func (keyExchange *KeyExchange) marshal() ([]byte, error) { - msgLog.Info("[KeyExchange] marshal(): Start marshalling") - - keyExchangeData := make([]byte, 4) - - binary.BigEndian.PutUint16(keyExchangeData[0:2], keyExchange.DiffieHellmanGroup) - keyExchangeData = append(keyExchangeData, keyExchange.KeyExchangeData...) - - return keyExchangeData, nil -} - -func (keyExchange *KeyExchange) unmarshal(rawData []byte) error { - msgLog.Info("[KeyExchange] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[KeyExchange] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[KeyExchange] unmarshal(): Unmarshal 1 key exchange data") - // bounds checking - if len(rawData) <= 4 { - return errors.New("KeyExchange: No sufficient bytes to decode next key exchange data") - } - - keyExchange.DiffieHellmanGroup = binary.BigEndian.Uint16(rawData[0:2]) - keyExchange.KeyExchangeData = append(keyExchange.KeyExchangeData, rawData[4:]...) - } - - return nil -} - -// Definition of Identification - Initiator - -var _ IKEPayload = &IdentificationInitiator{} - -type IdentificationInitiator struct { - IDType uint8 - IDData []byte -} - -func (identification *IdentificationInitiator) Type() types.IKEPayloadType { return types.TypeIDi } - -func (identification *IdentificationInitiator) marshal() ([]byte, error) { - msgLog.Info("[Identification] marshal(): Start marshalling") - - identificationData := make([]byte, 4) - - identificationData[0] = identification.IDType - identificationData = append(identificationData, identification.IDData...) - - return identificationData, nil -} - -func (identification *IdentificationInitiator) unmarshal(rawData []byte) error { - msgLog.Info("[Identification] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[Identification] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[Identification] unmarshal(): Unmarshal 1 identification") - // bounds checking - if len(rawData) <= 4 { - return errors.New("Identification: No sufficient bytes to decode next identification") - } - - identification.IDType = rawData[0] - identification.IDData = append(identification.IDData, rawData[4:]...) - } - - return nil -} - -// Definition of Identification - Responder - -var _ IKEPayload = &IdentificationResponder{} - -type IdentificationResponder struct { - IDType uint8 - IDData []byte -} - -func (identification *IdentificationResponder) Type() types.IKEPayloadType { return types.TypeIDr } - -func (identification *IdentificationResponder) marshal() ([]byte, error) { - msgLog.Info("[Identification] marshal(): Start marshalling") - - identificationData := make([]byte, 4) - - identificationData[0] = identification.IDType - identificationData = append(identificationData, identification.IDData...) - - return identificationData, nil -} - -func (identification *IdentificationResponder) unmarshal(rawData []byte) error { - msgLog.Info("[Identification] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[Identification] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[Identification] unmarshal(): Unmarshal 1 identification") - // bounds checking - if len(rawData) <= 4 { - return errors.New("Identification: No sufficient bytes to decode next identification") - } - - identification.IDType = rawData[0] - identification.IDData = append(identification.IDData, rawData[4:]...) - } - - return nil -} - -// Definition of Certificate - -var _ IKEPayload = &Certificate{} - -type Certificate struct { - CertificateEncoding uint8 - CertificateData []byte -} - -func (certificate *Certificate) Type() types.IKEPayloadType { return types.TypeCERT } - -func (certificate *Certificate) marshal() ([]byte, error) { - msgLog.Info("[Certificate] marshal(): Start marshalling") - - certificateData := make([]byte, 1) - - certificateData[0] = certificate.CertificateEncoding - certificateData = append(certificateData, certificate.CertificateData...) - - return certificateData, nil -} - -func (certificate *Certificate) unmarshal(rawData []byte) error { - msgLog.Info("[Certificate] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[Certificate] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[Certificate] unmarshal(): Unmarshal 1 certificate") - // bounds checking - if len(rawData) <= 1 { - return errors.New("Certificate: No sufficient bytes to decode next certificate") - } - - certificate.CertificateEncoding = rawData[0] - certificate.CertificateData = append(certificate.CertificateData, rawData[1:]...) - } - - return nil -} - -// Definition of Certificate Request - -var _ IKEPayload = &CertificateRequest{} - -type CertificateRequest struct { - CertificateEncoding uint8 - CertificationAuthority []byte -} - -func (certificateRequest *CertificateRequest) Type() types.IKEPayloadType { return types.TypeCERTreq } - -func (certificateRequest *CertificateRequest) marshal() ([]byte, error) { - msgLog.Info("[CertificateRequest] marshal(): Start marshalling") - - certificateRequestData := make([]byte, 1) - - certificateRequestData[0] = certificateRequest.CertificateEncoding - certificateRequestData = append(certificateRequestData, certificateRequest.CertificationAuthority...) - - return certificateRequestData, nil -} - -func (certificateRequest *CertificateRequest) unmarshal(rawData []byte) error { - msgLog.Info("[CertificateRequest] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[CertificateRequest] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[CertificateRequest] unmarshal(): Unmarshal 1 certificate request") - // bounds checking - if len(rawData) <= 1 { - return errors.New("CertificateRequest: No sufficient bytes to decode next certificate request") - } - - certificateRequest.CertificateEncoding = rawData[0] - certificateRequest.CertificationAuthority = append(certificateRequest.CertificationAuthority, rawData[1:]...) - } - - return nil -} - -// Definition of Authentication - -var _ IKEPayload = &Authentication{} - -type Authentication struct { - AuthenticationMethod uint8 - AuthenticationData []byte -} - -func (authentication *Authentication) Type() types.IKEPayloadType { return types.TypeAUTH } - -func (authentication *Authentication) marshal() ([]byte, error) { - msgLog.Info("[Authentication] marshal(): Start marshalling") - - authenticationData := make([]byte, 4) - - authenticationData[0] = authentication.AuthenticationMethod - authenticationData = append(authenticationData, authentication.AuthenticationData...) - - return authenticationData, nil -} - -func (authentication *Authentication) unmarshal(rawData []byte) error { - msgLog.Info("[Authentication] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[Authentication] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[Authentication] unmarshal(): Unmarshal 1 authentication") - // bounds checking - if len(rawData) <= 4 { - return errors.New("Authentication: No sufficient bytes to decode next authentication") - } - - authentication.AuthenticationMethod = rawData[0] - authentication.AuthenticationData = append(authentication.AuthenticationData, rawData[4:]...) - } - - return nil -} - -// Definition of Nonce - -var _ IKEPayload = &Nonce{} - -type Nonce struct { - NonceData []byte -} - -func (nonce *Nonce) Type() types.IKEPayloadType { return types.TypeNiNr } - -func (nonce *Nonce) marshal() ([]byte, error) { - msgLog.Info("[Nonce] marshal(): Start marshalling") - - nonceData := make([]byte, 0) - nonceData = append(nonceData, nonce.NonceData...) - - return nonceData, nil -} - -func (nonce *Nonce) unmarshal(rawData []byte) error { - msgLog.Info("[Nonce] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[Nonce] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[Nonce] unmarshal(): Unmarshal 1 nonce") - nonce.NonceData = append(nonce.NonceData, rawData...) - } - - return nil -} - -// Definition of Notification - -var _ IKEPayload = &Notification{} - -type Notification struct { - ProtocolID uint8 - NotifyMessageType uint16 - SPI []byte - NotificationData []byte -} - -func (notification *Notification) Type() types.IKEPayloadType { return types.TypeN } - -func (notification *Notification) marshal() ([]byte, error) { - msgLog.Info("[Notification] marshal(): Start marshalling") - - notificationData := make([]byte, 4) - - notificationData[0] = notification.ProtocolID - notificationData[1] = uint8(len(notification.SPI)) - binary.BigEndian.PutUint16(notificationData[2:4], notification.NotifyMessageType) - - notificationData = append(notificationData, notification.SPI...) - notificationData = append(notificationData, notification.NotificationData...) - - return notificationData, nil -} - -func (notification *Notification) unmarshal(rawData []byte) error { - msgLog.Info("[Notification] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[Notification] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[Notification] unmarshal(): Unmarshal 1 notification") - // bounds checking - if len(rawData) < 4 { - return errors.New("Notification: No sufficient bytes to decode next notification") - } - spiSize := rawData[1] - if len(rawData) < int(4+spiSize) { - return errors.New("Notification: No sufficient bytes to get SPI according to the length specified in header") - } - - notification.ProtocolID = rawData[0] - notification.NotifyMessageType = binary.BigEndian.Uint16(rawData[2:4]) - - notification.SPI = append(notification.SPI, rawData[4:4+spiSize]...) - notification.NotificationData = append(notification.NotificationData, rawData[4+spiSize:]...) - } - - return nil -} - -// Definition of Delete - -var _ IKEPayload = &Delete{} - -type Delete struct { - ProtocolID uint8 - SPISize uint8 - NumberOfSPI uint16 - SPIs []byte -} - -func (d *Delete) Type() types.IKEPayloadType { return types.TypeD } - -func (d *Delete) marshal() ([]byte, error) { - msgLog.Info("[Delete] marshal(): Start marshalling") - - if len(d.SPIs) != (int(d.SPISize) * int(d.NumberOfSPI)) { - return nil, fmt.Errorf("Total bytes of all SPIs not correct") - } - - deleteData := make([]byte, 4) - - deleteData[0] = d.ProtocolID - deleteData[1] = d.SPISize - binary.BigEndian.PutUint16(deleteData[2:4], d.NumberOfSPI) - - deleteData = append(deleteData, d.SPIs...) - - return deleteData, nil -} - -func (d *Delete) unmarshal(rawData []byte) error { - msgLog.Info("[Delete] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[Delete] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[Delete] unmarshal(): Unmarshal 1 delete") - // bounds checking - if len(rawData) <= 4 { - return errors.New("Delete: No sufficient bytes to decode next delete") - } - spiSize := rawData[1] - numberOfSPI := binary.BigEndian.Uint16(rawData[2:4]) - if len(rawData) < (4 + (int(spiSize) * int(numberOfSPI))) { - return errors.New("Delete: No Sufficient bytes to get SPIs according to the length specified in header") - } - - d.ProtocolID = rawData[0] - d.SPISize = spiSize - d.NumberOfSPI = numberOfSPI - - d.SPIs = append(d.SPIs, rawData[4:]...) - } - - return nil -} - -// Definition of Vendor ID - -var _ IKEPayload = &VendorID{} - -type VendorID struct { - VendorIDData []byte -} - -func (vendorID *VendorID) Type() types.IKEPayloadType { return types.TypeV } - -func (vendorID *VendorID) marshal() ([]byte, error) { - msgLog.Info("[VendorID] marshal(): Start marshalling") - return vendorID.VendorIDData, nil -} - -func (vendorID *VendorID) unmarshal(rawData []byte) error { - msgLog.Info("[VendorID] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[VendorID] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[VendorID] unmarshal(): Unmarshal 1 vendor ID") - vendorID.VendorIDData = append(vendorID.VendorIDData, rawData...) - } - - return nil -} - -// Definition of Traffic Selector - Initiator - -var _ IKEPayload = &TrafficSelectorInitiator{} - -type TrafficSelectorInitiator struct { - TrafficSelectors IndividualTrafficSelectorContainer -} - -type IndividualTrafficSelectorContainer []*IndividualTrafficSelector - -type IndividualTrafficSelector struct { - TSType uint8 - IPProtocolID uint8 - StartPort uint16 - EndPort uint16 - StartAddress []byte - EndAddress []byte -} - -func (trafficSelector *TrafficSelectorInitiator) Type() types.IKEPayloadType { return types.TypeTSi } - -func (trafficSelector *TrafficSelectorInitiator) marshal() ([]byte, error) { - msgLog.Info("[TrafficSelector] marshal(): Start marshalling") - - if len(trafficSelector.TrafficSelectors) > 0 { - trafficSelectorData := make([]byte, 4) - trafficSelectorData[0] = uint8(len(trafficSelector.TrafficSelectors)) - - for _, individualTrafficSelector := range trafficSelector.TrafficSelectors { - if individualTrafficSelector.TSType == types.TS_IPV4_ADDR_RANGE { - // Address length checking - if len(individualTrafficSelector.StartAddress) != 4 { - msgLog.Errorf("Address length %d", len(individualTrafficSelector.StartAddress)) - return nil, errors.New("TrafficSelector: Start IPv4 address length is not correct") - } - if len(individualTrafficSelector.EndAddress) != 4 { - return nil, errors.New("TrafficSelector: End IPv4 address length is not correct") - } - - individualTrafficSelectorData := make([]byte, 8) - - individualTrafficSelectorData[0] = individualTrafficSelector.TSType - individualTrafficSelectorData[1] = individualTrafficSelector.IPProtocolID - binary.BigEndian.PutUint16(individualTrafficSelectorData[4:6], individualTrafficSelector.StartPort) - binary.BigEndian.PutUint16(individualTrafficSelectorData[6:8], individualTrafficSelector.EndPort) - - individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.StartAddress...) - individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.EndAddress...) - - binary.BigEndian.PutUint16(individualTrafficSelectorData[2:4], uint16(len(individualTrafficSelectorData))) - - trafficSelectorData = append(trafficSelectorData, individualTrafficSelectorData...) - } else if individualTrafficSelector.TSType == types.TS_IPV6_ADDR_RANGE { - // Address length checking - if len(individualTrafficSelector.StartAddress) != 16 { - return nil, errors.New("TrafficSelector: Start IPv6 address length is not correct") - } - if len(individualTrafficSelector.EndAddress) != 16 { - return nil, errors.New("TrafficSelector: End IPv6 address length is not correct") - } - - individualTrafficSelectorData := make([]byte, 8) - - individualTrafficSelectorData[0] = individualTrafficSelector.TSType - individualTrafficSelectorData[1] = individualTrafficSelector.IPProtocolID - binary.BigEndian.PutUint16(individualTrafficSelectorData[4:6], individualTrafficSelector.StartPort) - binary.BigEndian.PutUint16(individualTrafficSelectorData[6:8], individualTrafficSelector.EndPort) - - individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.StartAddress...) - individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.EndAddress...) - - binary.BigEndian.PutUint16(individualTrafficSelectorData[2:4], uint16(len(individualTrafficSelectorData))) - - trafficSelectorData = append(trafficSelectorData, individualTrafficSelectorData...) - } else { - return nil, errors.New("TrafficSelector: Unsupported traffic selector type") - } - } - - return trafficSelectorData, nil - } else { - return nil, errors.New("TrafficSelector: Contains no traffic selector for marshalling message") - } -} - -func (trafficSelector *TrafficSelectorInitiator) unmarshal(rawData []byte) error { - msgLog.Info("[TrafficSelector] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[TrafficSelector] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[TrafficSelector] unmarshal(): Unmarshal 1 traffic selector") - // bounds checking - if len(rawData) < 4 { - return errors.New("TrafficSelector: No sufficient bytes to get number of traffic selector in header") - } - - numberOfSPI := rawData[0] - - rawData = rawData[4:] - - for ; numberOfSPI > 0; numberOfSPI-- { - // bounds checking - if len(rawData) < 4 { - return errors.New( - "TrafficSelector: No sufficient bytes to decode next individual traffic selector length in header") - } - trafficSelectorType := rawData[0] - if trafficSelectorType == types.TS_IPV4_ADDR_RANGE { - selectorLength := binary.BigEndian.Uint16(rawData[2:4]) - if selectorLength != 16 { - return errors.New("TrafficSelector: A TS_IPV4_ADDR_RANGE type traffic selector should has length 16 bytes") - } - if len(rawData) < int(selectorLength) { - return errors.New("TrafficSelector: No sufficient bytes to decode next individual traffic selector") - } - - individualTrafficSelector := &IndividualTrafficSelector{} - - individualTrafficSelector.TSType = rawData[0] - individualTrafficSelector.IPProtocolID = rawData[1] - individualTrafficSelector.StartPort = binary.BigEndian.Uint16(rawData[4:6]) - individualTrafficSelector.EndPort = binary.BigEndian.Uint16(rawData[6:8]) - - individualTrafficSelector.StartAddress = append(individualTrafficSelector.StartAddress, rawData[8:12]...) - individualTrafficSelector.EndAddress = append(individualTrafficSelector.EndAddress, rawData[12:16]...) - - trafficSelector.TrafficSelectors = append(trafficSelector.TrafficSelectors, individualTrafficSelector) - - rawData = rawData[16:] - } else if trafficSelectorType == types.TS_IPV6_ADDR_RANGE { - selectorLength := binary.BigEndian.Uint16(rawData[2:4]) - if selectorLength != 40 { - return errors.New("TrafficSelector: A TS_IPV6_ADDR_RANGE type traffic selector should has length 40 bytes") - } - if len(rawData) < int(selectorLength) { - return errors.New("TrafficSelector: No sufficient bytes to decode next individual traffic selector") - } - - individualTrafficSelector := &IndividualTrafficSelector{} - - individualTrafficSelector.TSType = rawData[0] - individualTrafficSelector.IPProtocolID = rawData[1] - individualTrafficSelector.StartPort = binary.BigEndian.Uint16(rawData[4:6]) - individualTrafficSelector.EndPort = binary.BigEndian.Uint16(rawData[6:8]) - - individualTrafficSelector.StartAddress = append(individualTrafficSelector.StartAddress, rawData[8:24]...) - individualTrafficSelector.EndAddress = append(individualTrafficSelector.EndAddress, rawData[24:40]...) - - trafficSelector.TrafficSelectors = append(trafficSelector.TrafficSelectors, individualTrafficSelector) - - rawData = rawData[40:] - } else { - return errors.New("TrafficSelector: Unsupported traffic selector type") - } - } - } - - return nil -} - -// Definition of Traffic Selector - Responder - -var _ IKEPayload = &TrafficSelectorResponder{} - -type TrafficSelectorResponder struct { - TrafficSelectors IndividualTrafficSelectorContainer -} - -func (trafficSelector *TrafficSelectorResponder) Type() types.IKEPayloadType { return types.TypeTSr } - -func (trafficSelector *TrafficSelectorResponder) marshal() ([]byte, error) { - msgLog.Info("[TrafficSelector] marshal(): Start marshalling") - - if len(trafficSelector.TrafficSelectors) > 0 { - trafficSelectorData := make([]byte, 4) - trafficSelectorData[0] = uint8(len(trafficSelector.TrafficSelectors)) - - for _, individualTrafficSelector := range trafficSelector.TrafficSelectors { - if individualTrafficSelector.TSType == types.TS_IPV4_ADDR_RANGE { - // Address length checking - if len(individualTrafficSelector.StartAddress) != 4 { - return nil, errors.New("TrafficSelector: Start IPv4 address length is not correct") - } - if len(individualTrafficSelector.EndAddress) != 4 { - return nil, errors.New("TrafficSelector: End IPv4 address length is not correct") - } - - individualTrafficSelectorData := make([]byte, 8) - - individualTrafficSelectorData[0] = individualTrafficSelector.TSType - individualTrafficSelectorData[1] = individualTrafficSelector.IPProtocolID - binary.BigEndian.PutUint16(individualTrafficSelectorData[4:6], individualTrafficSelector.StartPort) - binary.BigEndian.PutUint16(individualTrafficSelectorData[6:8], individualTrafficSelector.EndPort) - - individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.StartAddress...) - individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.EndAddress...) - - binary.BigEndian.PutUint16(individualTrafficSelectorData[2:4], uint16(len(individualTrafficSelectorData))) - - trafficSelectorData = append(trafficSelectorData, individualTrafficSelectorData...) - } else if individualTrafficSelector.TSType == types.TS_IPV6_ADDR_RANGE { - // Address length checking - if len(individualTrafficSelector.StartAddress) != 16 { - return nil, errors.New("TrafficSelector: Start IPv6 address length is not correct") - } - if len(individualTrafficSelector.EndAddress) != 16 { - return nil, errors.New("TrafficSelector: End IPv6 address length is not correct") - } - - individualTrafficSelectorData := make([]byte, 8) - - individualTrafficSelectorData[0] = individualTrafficSelector.TSType - individualTrafficSelectorData[1] = individualTrafficSelector.IPProtocolID - binary.BigEndian.PutUint16(individualTrafficSelectorData[4:6], individualTrafficSelector.StartPort) - binary.BigEndian.PutUint16(individualTrafficSelectorData[6:8], individualTrafficSelector.EndPort) - - individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.StartAddress...) - individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.EndAddress...) - - binary.BigEndian.PutUint16(individualTrafficSelectorData[2:4], uint16(len(individualTrafficSelectorData))) - - trafficSelectorData = append(trafficSelectorData, individualTrafficSelectorData...) - } else { - return nil, errors.New("TrafficSelector: Unsupported traffic selector type") - } - } - - return trafficSelectorData, nil - } else { - return nil, errors.New("TrafficSelector: Contains no traffic selector for marshalling message") - } -} - -func (trafficSelector *TrafficSelectorResponder) unmarshal(rawData []byte) error { - msgLog.Info("[TrafficSelector] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[TrafficSelector] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[TrafficSelector] unmarshal(): Unmarshal 1 traffic selector") - // bounds checking - if len(rawData) < 4 { - return errors.New("TrafficSelector: No sufficient bytes to get number of traffic selector in header") - } - - numberOfSPI := rawData[0] - - rawData = rawData[4:] - - for ; numberOfSPI > 0; numberOfSPI-- { - // bounds checking - if len(rawData) < 4 { - return errors.New( - "TrafficSelector: No sufficient bytes to decode next individual traffic selector length in header") - } - trafficSelectorType := rawData[0] - if trafficSelectorType == types.TS_IPV4_ADDR_RANGE { - selectorLength := binary.BigEndian.Uint16(rawData[2:4]) - if selectorLength != 16 { - return errors.New("TrafficSelector: A TS_IPV4_ADDR_RANGE type traffic selector should has length 16 bytes") - } - if len(rawData) < int(selectorLength) { - return errors.New("TrafficSelector: No sufficient bytes to decode next individual traffic selector") - } - - individualTrafficSelector := &IndividualTrafficSelector{} - - individualTrafficSelector.TSType = rawData[0] - individualTrafficSelector.IPProtocolID = rawData[1] - individualTrafficSelector.StartPort = binary.BigEndian.Uint16(rawData[4:6]) - individualTrafficSelector.EndPort = binary.BigEndian.Uint16(rawData[6:8]) - - individualTrafficSelector.StartAddress = append(individualTrafficSelector.StartAddress, rawData[8:12]...) - individualTrafficSelector.EndAddress = append(individualTrafficSelector.EndAddress, rawData[12:16]...) - - trafficSelector.TrafficSelectors = append(trafficSelector.TrafficSelectors, individualTrafficSelector) - - rawData = rawData[16:] - } else if trafficSelectorType == types.TS_IPV6_ADDR_RANGE { - selectorLength := binary.BigEndian.Uint16(rawData[2:4]) - if selectorLength != 40 { - return errors.New("TrafficSelector: A TS_IPV6_ADDR_RANGE type traffic selector should has length 40 bytes") - } - if len(rawData) < int(selectorLength) { - return errors.New("TrafficSelector: No sufficient bytes to decode next individual traffic selector") - } - - individualTrafficSelector := &IndividualTrafficSelector{} - - individualTrafficSelector.TSType = rawData[0] - individualTrafficSelector.IPProtocolID = rawData[1] - individualTrafficSelector.StartPort = binary.BigEndian.Uint16(rawData[4:6]) - individualTrafficSelector.EndPort = binary.BigEndian.Uint16(rawData[6:8]) - - individualTrafficSelector.StartAddress = append(individualTrafficSelector.StartAddress, rawData[8:24]...) - individualTrafficSelector.EndAddress = append(individualTrafficSelector.EndAddress, rawData[24:40]...) - - trafficSelector.TrafficSelectors = append(trafficSelector.TrafficSelectors, individualTrafficSelector) - - rawData = rawData[40:] - } else { - return errors.New("TrafficSelector: Unsupported traffic selector type") - } - } - } - - return nil -} - -// Definition of Encrypted Payload - -var _ IKEPayload = &Encrypted{} - -type Encrypted struct { - NextPayload uint8 - EncryptedData []byte -} - -func (encrypted *Encrypted) Type() types.IKEPayloadType { return types.TypeSK } - -func (encrypted *Encrypted) marshal() ([]byte, error) { - msgLog.Info("[Encrypted] marshal(): Start marshalling") - - if len(encrypted.EncryptedData) == 0 { - msgLog.Warn("[Encrypted] The encrypted data is empty") - } - - return encrypted.EncryptedData, nil -} - -func (encrypted *Encrypted) unmarshal(rawData []byte) error { - msgLog.Info("[Encrypted] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[Encrypted] unmarshal(): Payload length %d bytes", len(rawData)) - encrypted.EncryptedData = append(encrypted.EncryptedData, rawData...) - return nil -} - -// Definition of Configuration - -var _ IKEPayload = &Configuration{} - -type Configuration struct { - ConfigurationType uint8 - ConfigurationAttribute ConfigurationAttributeContainer -} - -type ConfigurationAttributeContainer []*IndividualConfigurationAttribute - -type IndividualConfigurationAttribute struct { - Type uint16 - Value []byte -} - -func (configuration *Configuration) Type() types.IKEPayloadType { return types.TypeCP } - -func (configuration *Configuration) marshal() ([]byte, error) { - msgLog.Info("[Configuration] marshal(): Start marshalling") - - configurationData := make([]byte, 4) - - configurationData[0] = configuration.ConfigurationType - - for _, attribute := range configuration.ConfigurationAttribute { - individualConfigurationAttributeData := make([]byte, 4) - - binary.BigEndian.PutUint16(individualConfigurationAttributeData[0:2], (attribute.Type & 0x7fff)) - binary.BigEndian.PutUint16(individualConfigurationAttributeData[2:4], uint16(len(attribute.Value))) - - individualConfigurationAttributeData = append(individualConfigurationAttributeData, attribute.Value...) - - configurationData = append(configurationData, individualConfigurationAttributeData...) - } - - return configurationData, nil -} - -func (configuration *Configuration) unmarshal(rawData []byte) error { - msgLog.Info("[Configuration] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[Configuration] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[Configuration] unmarshal(): Unmarshal 1 configuration") - // bounds checking - if len(rawData) <= 4 { - return errors.New("Configuration: No sufficient bytes to decode next configuration") - } - configuration.ConfigurationType = rawData[0] - - configurationAttributeData := rawData[4:] - - for len(configurationAttributeData) > 0 { - msgLog.Trace("[Configuration] unmarshal(): Unmarshal 1 configuration attribute") - // bounds checking - if len(configurationAttributeData) < 4 { - return errors.New("ConfigurationAttribute: No sufficient bytes to decode next configuration attribute") - } - length := binary.BigEndian.Uint16(configurationAttributeData[2:4]) - if len(configurationAttributeData) < int(4+length) { - return errors.New("ConfigurationAttribute: TLV attribute length error") - } - - individualConfigurationAttribute := new(IndividualConfigurationAttribute) - - individualConfigurationAttribute.Type = binary.BigEndian.Uint16(configurationAttributeData[0:2]) - configurationAttributeData = configurationAttributeData[4:] - individualConfigurationAttribute.Value = append( - individualConfigurationAttribute.Value, - configurationAttributeData[:length]...) - configurationAttributeData = configurationAttributeData[length:] - - configuration.ConfigurationAttribute = append(configuration.ConfigurationAttribute, individualConfigurationAttribute) - } - } - - return nil -} - -// Definition of IKE EAP - -var _ IKEPayload = &EAP{} - -type EAP struct { - Code uint8 - Identifier uint8 - EAPTypeData EAPTypeDataContainer -} - -func (eap *EAP) Type() types.IKEPayloadType { return types.TypeEAP } - -func (eap *EAP) marshal() ([]byte, error) { - msgLog.Info("[EAP] marshal(): Start marshalling") - - eapData := make([]byte, 4) - - eapData[0] = eap.Code - eapData[1] = eap.Identifier - - if len(eap.EAPTypeData) > 0 { - eapTypeData, err := eap.EAPTypeData[0].marshal() - if err != nil { - return nil, fmt.Errorf("EAP: EAP type data marshal failed: %+v", err) - } - - eapData = append(eapData, eapTypeData...) - } - - binary.BigEndian.PutUint16(eapData[2:4], uint16(len(eapData))) - - return eapData, nil -} - -func (eap *EAP) unmarshal(rawData []byte) error { - msgLog.Info("[EAP] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[EAP] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - msgLog.Trace("[EAP] unmarshal(): Unmarshal 1 EAP") - // bounds checking - if len(rawData) < 4 { - return errors.New("EAP: No sufficient bytes to decode next EAP payload") - } - eapPayloadLength := binary.BigEndian.Uint16(rawData[2:4]) - if eapPayloadLength < 4 { - return errors.New("EAP: Payload length specified in the header is too small for EAP") - } - if len(rawData) != int(eapPayloadLength) { - return errors.New("EAP: Received payload length not matches the length specified in header") - } - - eap.Code = rawData[0] - eap.Identifier = rawData[1] - - // EAP Success or Failed - if eapPayloadLength == 4 { - return nil - } - - eapType := rawData[4] - var eapTypeData EAPTypeFormat - - switch eapType { - case types.EAPTypeIdentity: - eapTypeData = new(EAPIdentity) - case types.EAPTypeNotification: - eapTypeData = new(EAPNotification) - case types.EAPTypeNak: - eapTypeData = new(EAPNak) - case types.EAPTypeExpanded: - eapTypeData = new(EAPExpanded) - default: - // TODO: Create unsupprted type to handle it - return errors.New("EAP: Not supported EAP type") - } - - if err := eapTypeData.unmarshal(rawData[4:]); err != nil { - return fmt.Errorf("EAP: Unamrshal EAP type data failed: %+v", err) - } - - eap.EAPTypeData = append(eap.EAPTypeData, eapTypeData) - } - - return nil -} - -type EAPTypeDataContainer []EAPTypeFormat - -type EAPTypeFormat interface { - // Type specifies EAP types - Type() types.EAPType - - // Called by EAP.marshal() or EAP.unmarshal() - marshal() ([]byte, error) - unmarshal(rawData []byte) error -} - -// Definition of EAP Identity - -var _ EAPTypeFormat = &EAPIdentity{} - -type EAPIdentity struct { - IdentityData []byte -} - -func (eapIdentity *EAPIdentity) Type() types.EAPType { return types.EAPTypeIdentity } - -func (eapIdentity *EAPIdentity) marshal() ([]byte, error) { - msgLog.Info("[EAP][Identity] marshal(): Start marshalling") - - if len(eapIdentity.IdentityData) == 0 { - return nil, errors.New("EAPIdentity: EAP identity is empty") - } - - eapIdentityData := []byte{types.EAPTypeIdentity} - eapIdentityData = append(eapIdentityData, eapIdentity.IdentityData...) - - return eapIdentityData, nil -} - -func (eapIdentity *EAPIdentity) unmarshal(rawData []byte) error { - msgLog.Info("[EAP][Identity] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[EAP][Identity] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 1 { - eapIdentity.IdentityData = append(eapIdentity.IdentityData, rawData[1:]...) - } - - return nil -} - -// Definition of EAP Notification - -var _ EAPTypeFormat = &EAPNotification{} - -type EAPNotification struct { - NotificationData []byte -} - -func (eapNotification *EAPNotification) Type() types.EAPType { return types.EAPTypeNotification } - -func (eapNotification *EAPNotification) marshal() ([]byte, error) { - msgLog.Info("[EAP][Notification] marshal(): Start marshalling") - - if len(eapNotification.NotificationData) == 0 { - return nil, errors.New("EAPNotification: EAP notification is empty") - } - - eapNotificationData := []byte{types.EAPTypeNotification} - eapNotificationData = append(eapNotificationData, eapNotification.NotificationData...) - - return eapNotificationData, nil -} - -func (eapNotification *EAPNotification) unmarshal(rawData []byte) error { - msgLog.Info("[EAP][Notification] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[EAP][Notification] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 1 { - eapNotification.NotificationData = append(eapNotification.NotificationData, rawData[1:]...) - } - - return nil -} - -// Definition of EAP Nak - -var _ EAPTypeFormat = &EAPNak{} - -type EAPNak struct { - NakData []byte -} - -func (eapNak *EAPNak) Type() types.EAPType { return types.EAPTypeNak } - -func (eapNak *EAPNak) marshal() ([]byte, error) { - msgLog.Info("[EAP][Nak] marshal(): Start marshalling") - - if len(eapNak.NakData) == 0 { - return nil, errors.New("EAPNak: EAP nak is empty") - } - - eapNakData := []byte{types.EAPTypeNak} - eapNakData = append(eapNakData, eapNak.NakData...) - - return eapNakData, nil -} - -func (eapNak *EAPNak) unmarshal(rawData []byte) error { - msgLog.Info("[EAP][Nak] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[EAP][Nak] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 1 { - eapNak.NakData = append(eapNak.NakData, rawData[1:]...) - } - - return nil -} - -// Definition of EAP expanded - -var _ EAPTypeFormat = &EAPExpanded{} - -type EAPExpanded struct { - VendorID uint32 - VendorType uint32 - VendorData []byte -} - -func (eapExpanded *EAPExpanded) Type() types.EAPType { return types.EAPTypeExpanded } - -func (eapExpanded *EAPExpanded) marshal() ([]byte, error) { - msgLog.Info("[EAP][Expanded] marshal(): Start marshalling") - - eapExpandedData := make([]byte, 8) - - vendorID := eapExpanded.VendorID & 0x00ffffff - typeAndVendorID := (uint32(types.EAPTypeExpanded)<<24 | vendorID) - - binary.BigEndian.PutUint32(eapExpandedData[0:4], typeAndVendorID) - binary.BigEndian.PutUint32(eapExpandedData[4:8], eapExpanded.VendorType) - - if len(eapExpanded.VendorData) == 0 { - msgLog.Warn("[EAP][Expanded] marshal(): EAP vendor data field is empty") - return eapExpandedData, nil - } - - eapExpandedData = append(eapExpandedData, eapExpanded.VendorData...) - - return eapExpandedData, nil -} - -func (eapExpanded *EAPExpanded) unmarshal(rawData []byte) error { - msgLog.Info("[EAP][Expanded] unmarshal(): Start unmarshalling received bytes") - msgLog.Tracef("[EAP][Expanded] unmarshal(): Payload length %d bytes", len(rawData)) - - if len(rawData) > 0 { - if len(rawData) < 8 { - return errors.New("EAPExpanded: No sufficient bytes to decode the EAP expanded type") - } - - typeAndVendorID := binary.BigEndian.Uint32(rawData[0:4]) - eapExpanded.VendorID = typeAndVendorID & 0x00ffffff - - eapExpanded.VendorType = binary.BigEndian.Uint32(rawData[4:8]) - - if len(rawData) > 8 { - eapExpanded.VendorData = append(eapExpanded.VendorData, rawData[8:]...) - } - } - - return nil + unmarshal(b []byte) error } diff --git a/message/message_test.go b/message/message_test.go index 39173de..dd0540a 100644 --- a/message/message_test.go +++ b/message/message_test.go @@ -2,375 +2,10 @@ package message import ( "bytes" - "crypto/aes" - "crypto/cipher" - Crand "crypto/rand" - "encoding/binary" - "encoding/hex" - "io" - Mrand "math/rand" - "net" "testing" -) - -var conn net.Conn - -func init() { - if connTmp, err := net.Dial("udp", "127.0.0.1:500"); err != nil { - panic(err) - } else { - conn = connTmp - } -} - -// TestEncodeDecode tests the Encode() and Decode() function using the data -// build manually. -// First, build each payload with correct value, then the IKE message for -// IKE_SA_INIT type. -// Second, encode/decode the IKE message using Encode/Decode function, and then -// re-encode the decoded message again. -// Third, send the encoded data to the UDP connection for verification with Wireshark. -// Compare the dataFirstEncode and dataSecondEncode and return the result. -func TestEncodeDecode(t *testing.T) { - testPacket := &IKEMessage{} - - // random an SPI - src := Mrand.NewSource(63579) - localRand := Mrand.New(src) - ispi := localRand.Uint64() - - testPacket.InitiatorSPI = ispi - testPacket.Version = 0x20 - testPacket.ExchangeType = 34 // IKE_SA_INIT - testPacket.Flags = 16 // flagI is set - testPacket.MessageID = 0 // for IKE_SA_INIT - - testSA := &SecurityAssociation{} - - testProposal1 := &Proposal{} - testProposal1.ProposalNumber = 1 // first - testProposal1.ProtocolID = 1 // IKE - - testtransform1 := &Transform{} - testtransform1.TransformType = 1 // ENCR - testtransform1.TransformID = 12 // ENCR_AES_CBC - testtransform1.AttributePresent = true - testtransform1.AttributeFormat = 1 - testtransform1.AttributeType = 14 - testtransform1.AttributeValue = 128 - - testProposal1.EncryptionAlgorithm = append(testProposal1.EncryptionAlgorithm, testtransform1) - - testtransform2 := &Transform{} - testtransform2.TransformType = 1 // ENCR - testtransform2.TransformID = 12 // ENCR_AES_CBC - testtransform2.AttributePresent = true - testtransform2.AttributeFormat = 1 - testtransform2.AttributeType = 14 - testtransform2.AttributeValue = 192 - - testProposal1.EncryptionAlgorithm = append(testProposal1.EncryptionAlgorithm, testtransform2) - - testtransform3 := &Transform{} - testtransform3.TransformType = 3 // INTEG - testtransform3.TransformID = 5 // AUTH_AES_XCBC_96 - testtransform3.AttributePresent = false - - testProposal1.IntegrityAlgorithm = append(testProposal1.IntegrityAlgorithm, testtransform3) - - testtransform4 := &Transform{} - testtransform4.TransformType = 3 // INTEG - testtransform4.TransformID = 2 // AUTH_HMAC_SHA1_96 - testtransform4.AttributePresent = false - - testProposal1.IntegrityAlgorithm = append(testProposal1.IntegrityAlgorithm, testtransform4) - - testSA.Proposals = append(testSA.Proposals, testProposal1) - - testProposal2 := &Proposal{} - testProposal2.ProposalNumber = 2 // second - testProposal2.ProtocolID = 1 // IKE - - testtransform1 = &Transform{} - testtransform1.TransformType = 1 // ENCR - testtransform1.TransformID = 12 // ENCR_AES_CBC - testtransform1.AttributePresent = true - testtransform1.AttributeFormat = 1 - testtransform1.AttributeType = 14 - testtransform1.AttributeValue = 128 - - testProposal2.EncryptionAlgorithm = append(testProposal2.EncryptionAlgorithm, testtransform1) - - testtransform2 = &Transform{} - testtransform2.TransformType = 1 // ENCR - testtransform2.TransformID = 12 // ENCR_AES_CBC - testtransform2.AttributePresent = true - testtransform2.AttributeFormat = 1 - testtransform2.AttributeType = 14 - testtransform2.AttributeValue = 192 - - testProposal2.EncryptionAlgorithm = append(testProposal2.EncryptionAlgorithm, testtransform2) - - testtransform3 = &Transform{} - testtransform3.TransformType = 3 // INTEG - testtransform3.TransformID = 1 // AUTH_HMAC_MD5_96 - testtransform3.AttributePresent = false - - testProposal2.IntegrityAlgorithm = append(testProposal2.IntegrityAlgorithm, testtransform3) - - testtransform4 = &Transform{} - testtransform4.TransformType = 3 // INTEG - testtransform4.TransformID = 2 // AUTH_HMAC_SHA1_96 - testtransform4.AttributePresent = false - - testProposal2.IntegrityAlgorithm = append(testProposal2.IntegrityAlgorithm, testtransform4) - - testSA.Proposals = append(testSA.Proposals, testProposal2) - - testPacket.Payloads = append(testPacket.Payloads, testSA) - - testKE := &KeyExchange{} - - testKE.DiffieHellmanGroup = 1 - for i := 0; i < 8; i++ { - partKeyExchangeData := make([]byte, 8) - binary.BigEndian.PutUint64(partKeyExchangeData, 7482105748278537214) - testKE.KeyExchangeData = append(testKE.KeyExchangeData, partKeyExchangeData...) - } - - testPacket.Payloads = append(testPacket.Payloads, testKE) - - testIDr := &IdentificationResponder{} - - testIDr.IDType = 3 - for i := 0; i < 8; i++ { - partIdentification := make([]byte, 8) - binary.BigEndian.PutUint64(partIdentification, 4378215321473912643) - testIDr.IDData = append(testIDr.IDData, partIdentification...) - } - - testPacket.Payloads = append(testPacket.Payloads, testIDr) - - testCert := &Certificate{} - - testCert.CertificateEncoding = 1 - for i := 0; i < 8; i++ { - partCertificate := make([]byte, 8) - binary.BigEndian.PutUint64(partCertificate, 4378217432157543265) - testCert.CertificateData = append(testCert.CertificateData, partCertificate...) - } - - testPacket.Payloads = append(testPacket.Payloads, testCert) - - testCertReq := &CertificateRequest{} - - testCertReq.CertificateEncoding = 1 - for i := 0; i < 8; i++ { - partCertificateRquest := make([]byte, 8) - binary.BigEndian.PutUint64(partCertificateRquest, 7438274381754372584) - testCertReq.CertificationAuthority = append(testCertReq.CertificationAuthority, partCertificateRquest...) - } - - testPacket.Payloads = append(testPacket.Payloads, testCertReq) - - testAuth := &Authentication{} - - testAuth.AuthenticationMethod = 1 - for i := 0; i < 8; i++ { - partAuthentication := make([]byte, 8) - binary.BigEndian.PutUint64(partAuthentication, 4632714362816473824) - testAuth.AuthenticationData = append(testAuth.AuthenticationData, partAuthentication...) - } - - testPacket.Payloads = append(testPacket.Payloads, testAuth) - - testNonce := &Nonce{} - - for i := 0; i < 8; i++ { - partNonce := make([]byte, 8) - binary.BigEndian.PutUint64(partNonce, 8984327463782167381) - testNonce.NonceData = append(testNonce.NonceData, partNonce...) - } - - testPacket.Payloads = append(testPacket.Payloads, testNonce) - - testNotification := &Notification{} - - testNotification.ProtocolID = 1 - testNotification.NotifyMessageType = 2 - - for i := 0; i < 5; i++ { - partSPI := make([]byte, 8) - binary.BigEndian.PutUint64(partSPI, 4372847328749832794) - testNotification.SPI = append(testNotification.SPI, partSPI...) - } - - for i := 0; i < 19; i++ { - partNotification := make([]byte, 8) - binary.BigEndian.PutUint64(partNotification, 9721437148392747354) - testNotification.NotificationData = append(testNotification.NotificationData, partNotification...) - } - - testPacket.Payloads = append(testPacket.Payloads, testNotification) - - testDelete := &Delete{} - - testDelete.ProtocolID = 1 - testDelete.SPISize = 9 - testDelete.NumberOfSPI = 4 - - for i := 0; i < 36; i++ { - testDelete.SPIs = append(testDelete.SPIs, 87) - } - - testPacket.Payloads = append(testPacket.Payloads, testDelete) - - testVendor := &VendorID{} - - for i := 0; i < 5; i++ { - partVendorData := make([]byte, 8) - binary.BigEndian.PutUint64(partVendorData, 5421487329873941748) - testVendor.VendorIDData = append(testVendor.VendorIDData, partVendorData...) - } - - testPacket.Payloads = append(testPacket.Payloads, testVendor) - - testTSi := &TrafficSelectorResponder{} - - testIndividualTS := &IndividualTrafficSelector{} - - testIndividualTS.TSType = 7 - testIndividualTS.IPProtocolID = 6 - testIndividualTS.StartPort = 1989 - testIndividualTS.EndPort = 2020 - - testIndividualTS.StartAddress = []byte{192, 168, 0, 15} - testIndividualTS.EndAddress = []byte{192, 168, 0, 192} - - testTSi.TrafficSelectors = append(testTSi.TrafficSelectors, testIndividualTS) - - testIndividualTS = &IndividualTrafficSelector{} - - testIndividualTS.TSType = 8 - testIndividualTS.IPProtocolID = 6 - testIndividualTS.StartPort = 2010 - testIndividualTS.EndPort = 2050 - - testIndividualTS.StartAddress = net.ParseIP("2001:db8::68") - testIndividualTS.EndAddress = net.ParseIP("2001:db8::72") - - testTSi.TrafficSelectors = append(testTSi.TrafficSelectors, testIndividualTS) - testPacket.Payloads = append(testPacket.Payloads, testTSi) - - testCP := new(Configuration) - - testCP.ConfigurationType = 1 - - testIndividualConfigurationAttribute := new(IndividualConfigurationAttribute) - - testIndividualConfigurationAttribute.Type = 1 - testIndividualConfigurationAttribute.Value = []byte{10, 1, 14, 1} - - testCP.ConfigurationAttribute = append(testCP.ConfigurationAttribute, testIndividualConfigurationAttribute) - - testPacket.Payloads = append(testPacket.Payloads, testCP) - - testEAP := new(EAP) - - testEAP.Code = 1 - testEAP.Identifier = 123 - - testEAPExpanded := new(EAPExpanded) - - testEAPExpanded.VendorID = 26838 - testEAPExpanded.VendorType = 1 - testEAPExpanded.VendorData = []byte{9, 4, 8, 7} - - testEAPNotification := new(EAPNotification) - - rawstr := "I'm tired" - testEAPNotification.NotificationData = []byte(rawstr) - - testEAP.EAPTypeData = append(testEAP.EAPTypeData, testEAPNotification) - - testPacket.Payloads = append(testPacket.Payloads, testEAP) - - testSK := new(Encrypted) - - testSK.NextPayload = 33 - - ikePayload := IKEPayloadContainer{ - testSA, - testAuth, - } - - ikePayloadDataForSK, retErr := ikePayload.Encode() - if retErr != nil { - t.Fatalf("EncodePayload failed: %+v", retErr) - } - - // aes 128 key - key, retErr := hex.DecodeString("6368616e676520746869732070617373") - if retErr != nil { - t.Fatalf("HexDecoding failed: %+v", retErr) - } - block, retErr := aes.NewCipher(key) - if retErr != nil { - t.Fatalf("AES NewCipher failed: %+v", retErr) - } - - // padding plaintext - padNum := len(ikePayloadDataForSK) % aes.BlockSize - for i := 0; i < (aes.BlockSize - padNum); i++ { - ikePayloadDataForSK = append(ikePayloadDataForSK, byte(padNum)) - } - - // ciphertext - cipherText := make([]byte, aes.BlockSize+len(ikePayloadDataForSK)) - iv := cipherText[:aes.BlockSize] - if _, err := io.ReadFull(Crand.Reader, iv); err != nil { - t.Fatalf("IO ReadFull failed: %+v", err) - } - - // CBC mode - mode := cipher.NewCBCEncrypter(block, iv) - mode.CryptBlocks(cipherText[aes.BlockSize:], ikePayloadDataForSK) - - testSK.EncryptedData = cipherText - - testPacket.Payloads = append(testPacket.Payloads, testSK) - - var dataFirstEncode, dataSecondEncode []byte - var err error - decodedPacket := new(IKEMessage) - - if dataFirstEncode, err = testPacket.Encode(); err != nil { - t.Fatalf("Encode failed: %+v", err) - } - - t.Logf("%+v", dataFirstEncode) - - if err = decodedPacket.Decode(dataFirstEncode); err != nil { - t.Fatalf("Decode failed: %+v", err) - } - - if dataSecondEncode, err = decodedPacket.Encode(); err != nil { - t.Fatalf("Encode failed: %+v", err) - } - - t.Logf("Original IKE Message: %+v", dataFirstEncode) - t.Logf("Result IKE Message: %+v", dataSecondEncode) - - _, err = conn.Write(dataFirstEncode) - if err != nil { - t.Fatalf("Error: %+v", err) - } - - if !bytes.Equal(dataFirstEncode, dataSecondEncode) { - t.FailNow() - } -} + "github.com/stretchr/testify/require" +) // TestEncodeDecodeUsingPublicData tests the Encode() and Decode() function // using the public data. @@ -424,24 +59,385 @@ func TestEncodeDecodeUsingPublicData(t *testing.T) { 0xde, 0x7f, 0x00, 0xd6, 0xc2, 0xd3, } - dataCopy := make([]byte, len(data)) - copy(dataCopy, data) - - ikePacket := new(IKEMessage) - err := ikePacket.Decode(data) + ikeMsg := new(IKEMessage) + err := ikeMsg.Decode(data) if err != nil { t.Fatalf("Decode failed: %+v", err) } - verifyData, err := ikePacket.Encode() + verifyData, err := ikeMsg.Encode() if err != nil { t.Fatalf("Encode failed: %+v", err) } - if !bytes.Equal(dataCopy, data) { - t.FailNow() - } if !bytes.Equal(data, verifyData) { t.FailNow() } } + +var ( + validIKEINIT = &IKEMessage{ + IKEHeader: &IKEHeader{ + InitiatorSPI: 0x000000000006f708, + ResponderSPI: 0xc9e2e31f8b64053d, + MajorVersion: 2, + MinorVersion: 0, + ExchangeType: IKE_SA_INIT, + Flags: 0x08, + MessageID: 0x00, + NextPayload: uint8(TypeSA), + }, + Payloads: IKEPayloadContainer{ + &SecurityAssociation{ + Proposals: ProposalContainer{ + &Proposal{ + ProposalNumber: 2, + ProtocolID: 1, + SPI: []byte{1, 2, 3}, + EncryptionAlgorithm: TransformContainer{ + &Transform{ + TransformType: TypeEncryptionAlgorithm, + TransformID: ENCR_AES_CBC, + AttributePresent: true, + AttributeFormat: AttributeFormatUseTV, + AttributeType: AttributeTypeKeyLength, + AttributeValue: 128, + }, + }, + IntegrityAlgorithm: TransformContainer{ + &Transform{ + TransformType: TypeIntegrityAlgorithm, + TransformID: AUTH_HMAC_SHA2_256_128, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + DiffieHellmanGroup: TransformContainer{ + &Transform{ + TransformType: TypeDiffieHellmanGroup, + TransformID: DH_1024_BIT_MODP, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + PseudorandomFunction: TransformContainer{ + &Transform{ + TransformType: TypePseudorandomFunction, + TransformID: PRF_HMAC_SHA2_256, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + ExtendedSequenceNumbers: TransformContainer{ + &Transform{ + TransformType: TypeExtendedSequenceNumbers, + TransformID: ESN_DISABLE, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + }, + }, + }, + &Notification{ + ProtocolID: TypeNone, + NotifyMessageType: NAT_DETECTION_SOURCE_IP, + SPI: []byte{0x01, 0x02, 0x03}, + NotificationData: []byte{ + 0x50, 0xc4, 0xc2, 0xbe, 0x8e, 0x3f, 0xd9, 0x16, + 0x19, 0x24, 0x65, 0x0d, 0x14, 0x5d, 0x4f, 0xf6, + 0x46, 0xd8, 0x9d, 0x75, + }, + }, + &Notification{ + ProtocolID: TypeNone, + NotifyMessageType: NAT_DETECTION_DESTINATION_IP, + SPI: []byte{0x01, 0x02, 0x03}, + NotificationData: []byte{ + 0x50, 0xc4, 0xc2, 0xbe, 0x8e, 0x3f, 0xd9, 0x16, + 0x19, 0x24, 0x65, 0x0d, 0x14, 0x5d, 0x4f, 0xf6, + 0x46, 0xd8, 0x9d, 0x75, + }, + }, + }, + } + + validIKEINITByte = []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xf7, 0x08, + 0xc9, 0xe2, 0xe3, 0x1f, 0x8b, 0x64, 0x05, 0x3d, + 0x21, 0x20, 0x22, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x95, 0x29, 0x00, 0x00, 0x3b, + 0x00, 0x00, 0x00, 0x37, 0x02, 0x01, 0x03, 0x05, + 0x01, 0x02, 0x03, 0x03, 0x00, 0x00, 0x0c, 0x01, + 0x00, 0x00, 0x0c, 0x80, 0x0e, 0x00, 0x80, 0x03, + 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x05, 0x03, + 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x0c, 0x03, + 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x08, 0x05, 0x00, 0x00, 0x00, 0x29, + 0x00, 0x00, 0x1f, 0x00, 0x03, 0x40, 0x04, 0x01, + 0x02, 0x03, 0x50, 0xc4, 0xc2, 0xbe, 0x8e, 0x3f, + 0xd9, 0x16, 0x19, 0x24, 0x65, 0x0d, 0x14, 0x5d, + 0x4f, 0xf6, 0x46, 0xd8, 0x9d, 0x75, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x03, 0x40, 0x05, 0x01, 0x02, + 0x03, 0x50, 0xc4, 0xc2, 0xbe, 0x8e, 0x3f, 0xd9, + 0x16, 0x19, 0x24, 0x65, 0x0d, 0x14, 0x5d, 0x4f, + 0xf6, 0x46, 0xd8, 0x9d, 0x75, + } + + validCreateChildSA = &IKEMessage{ + IKEHeader: &IKEHeader{ + InitiatorSPI: 0x000000000006f708, + ResponderSPI: 0xc9e2e31f8b64053d, + MajorVersion: 2, + MinorVersion: 0, + ExchangeType: CREATE_CHILD_SA, + Flags: 0x00, + MessageID: 0x00, + NextPayload: uint8(TypeTSi), + }, + Payloads: IKEPayloadContainer{ + &TrafficSelectorInitiator{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + TSType: TS_IPV4_ADDR_RANGE, + IPProtocolID: IPProtocolAll, + StartPort: 0, + EndPort: 65535, + StartAddress: []byte{0x0a, 0x00, 0x00, 0x01}, + EndAddress: []byte{0x0a, 0x00, 0x00, 0x01}, + }, + }, + }, + &TrafficSelectorResponder{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + TSType: TS_IPV4_ADDR_RANGE, + IPProtocolID: IPProtocolAll, + StartPort: 0, + EndPort: 65535, + StartAddress: []byte{0x0a, 0x00, 0x00, 0x01}, + EndAddress: []byte{0x0a, 0x00, 0x00, 0x01}, + }, + }, + }, + &Notification{ + NotifyMessageType: Vendor3GPPNotifyType5G_QOS_INFO, + NotificationData: []byte{0x05, 0x01, 0x01, 0x01, 0x02}, + }, + }, + } + + validCreateChildSAByte = []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xf7, 0x08, + 0xc9, 0xe2, 0xe3, 0x1f, 0x8b, 0x64, 0x05, 0x3d, + 0x2c, 0x20, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x59, 0x2d, 0x00, 0x00, 0x18, + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x10, + 0x00, 0x00, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x01, + 0x0a, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x18, + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x10, + 0x00, 0x00, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x01, + 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x00, 0xd8, 0xcd, 0x05, 0x01, 0x01, 0x01, + 0x02, + } + + validInformation = &IKEMessage{ + IKEHeader: &IKEHeader{ + InitiatorSPI: 0x000000000006f708, + ResponderSPI: 0xc9e2e31f8b64053d, + MajorVersion: 2, + MinorVersion: 0, + ExchangeType: INFORMATIONAL, + Flags: 0x08, + MessageID: 0x07, + NextPayload: uint8(TypeN), + }, + Payloads: IKEPayloadContainer{ + &Notification{ + ProtocolID: TypeNone, + NotifyMessageType: NAT_DETECTION_SOURCE_IP, + SPI: []byte{0x01, 0x02, 0x03}, + NotificationData: []byte{ + 0x50, 0xc4, 0xc2, 0xbe, 0x8e, 0x3f, 0xd9, 0x16, + 0x19, 0x24, 0x65, 0x0d, 0x14, 0x5d, 0x4f, 0xf6, + 0x46, 0xd8, 0x9d, 0x75, + }, + }, + &Notification{ + ProtocolID: TypeNone, + NotifyMessageType: NAT_DETECTION_DESTINATION_IP, + SPI: []byte{0x01, 0x02, 0x03}, + NotificationData: []byte{ + 0xc4, 0xc2, 0xbe, 0x8e, 0x3f, 0xd9, 0x16, 0x19, + 0x24, 0x65, 0x0d, 0x14, 0x5d, 0x4f, 0xf6, 0x46, + }, + }, + }, + } + + validInformationByte = []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xf7, 0x08, + 0xc9, 0xe2, 0xe3, 0x1f, 0x8b, 0x64, 0x05, 0x3d, + 0x29, 0x20, 0x25, 0x08, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x56, 0x29, 0x00, 0x00, 0x1f, + 0x00, 0x03, 0x40, 0x04, 0x01, 0x02, 0x03, 0x50, + 0xc4, 0xc2, 0xbe, 0x8e, 0x3f, 0xd9, 0x16, 0x19, + 0x24, 0x65, 0x0d, 0x14, 0x5d, 0x4f, 0xf6, 0x46, + 0xd8, 0x9d, 0x75, 0x00, 0x00, 0x00, 0x1b, 0x00, + 0x03, 0x40, 0x05, 0x01, 0x02, 0x03, 0xc4, 0xc2, + 0xbe, 0x8e, 0x3f, 0xd9, 0x16, 0x19, 0x24, 0x65, + 0x0d, 0x14, 0x5d, 0x4f, 0xf6, 0x46, + } + + validIKEAUTH = &IKEMessage{ + IKEHeader: &IKEHeader{ + InitiatorSPI: 0x000000000006f708, + ResponderSPI: 0xc9e2e31f8b64053d, + MajorVersion: 2, + MinorVersion: 0, + ExchangeType: IKE_AUTH, + Flags: 0x08, + MessageID: 0x03, + NextPayload: uint8(TypeSK), + }, + Payloads: IKEPayloadContainer{ + &Encrypted{ + NextPayload: uint8(TypeEAP), + EncryptedData: []byte{ + 0xec, 0x50, 0x31, 0x16, 0x2c, 0x69, 0x2f, 0xbb, + 0xfc, 0x4d, 0x20, 0x64, 0x0c, 0x91, 0x21, 0xeb, + 0xe9, 0x47, 0x5e, 0xf9, 0x4f, 0x9b, 0x02, 0x95, + 0x9d, 0x31, 0x24, 0x2e, 0x53, 0x5e, 0x9c, 0x3c, + 0x4d, 0xca, 0xec, 0xd1, 0xbf, 0xd6, 0xdd, 0x80, + 0xaa, 0x81, 0x2b, 0x07, 0xde, 0x36, 0xde, 0xe9, + 0xb7, 0x50, 0x94, 0x35, 0xf6, 0x35, 0xe1, 0xaa, + 0xae, 0x1c, 0x38, 0x25, 0xf4, 0xea, 0xe3, 0x38, + 0x49, 0x03, 0xf7, 0x24, 0xf4, 0x44, 0x17, 0x0c, + 0x68, 0x45, 0xca, 0x80, + }, + }, + }, + } + validIKEAUTHByte = []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xf7, 0x08, + 0xc9, 0xe2, 0xe3, 0x1f, 0x8b, 0x64, 0x05, 0x3d, + 0x2e, 0x20, 0x23, 0x08, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x6c, 0x30, 0x00, 0x00, 0x50, + 0xec, 0x50, 0x31, 0x16, 0x2c, 0x69, 0x2f, 0xbb, + 0xfc, 0x4d, 0x20, 0x64, 0x0c, 0x91, 0x21, 0xeb, + 0xe9, 0x47, 0x5e, 0xf9, 0x4f, 0x9b, 0x02, 0x95, + 0x9d, 0x31, 0x24, 0x2e, 0x53, 0x5e, 0x9c, 0x3c, + 0x4d, 0xca, 0xec, 0xd1, 0xbf, 0xd6, 0xdd, 0x80, + 0xaa, 0x81, 0x2b, 0x07, 0xde, 0x36, 0xde, 0xe9, + 0xb7, 0x50, 0x94, 0x35, 0xf6, 0x35, 0xe1, 0xaa, + 0xae, 0x1c, 0x38, 0x25, 0xf4, 0xea, 0xe3, 0x38, + 0x49, 0x03, 0xf7, 0x24, 0xf4, 0x44, 0x17, 0x0c, + 0x68, 0x45, 0xca, 0x80, + } +) + +func TestDecode(t *testing.T) { + testcases := []struct { + description string + b []byte + expErr bool + expIkeMsg *IKEMessage + }{ + { + description: "decode IKE_INIT", + b: validIKEINITByte, + expIkeMsg: validIKEINIT, + expErr: false, + }, + { + description: "decode Create_CHILD_SA", + b: validCreateChildSAByte, + expIkeMsg: validCreateChildSA, + expErr: false, + }, + { + description: "decode INFORMATION", + b: validInformationByte, + expIkeMsg: validInformation, + expErr: false, + }, + { + description: "decode IKE_AUTH", + b: validIKEAUTHByte, + expIkeMsg: validIKEAUTH, + expErr: false, + }, + { + description: "decode with short length message", + b: []byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, + }, + expErr: true, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + ikeMsg := new(IKEMessage) + + err := ikeMsg.Decode(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expIkeMsg, ikeMsg) + } + }) + } +} + +func TestEncode(t *testing.T) { + testcases := []struct { + description string + ikeMsg *IKEMessage + expErr bool + expByte []byte + }{ + { + description: "IKE_INIT encode", + ikeMsg: validIKEINIT, + expErr: false, + expByte: validIKEINITByte, + }, + { + description: "IKE_AUTH encode", + ikeMsg: validIKEAUTH, + expErr: false, + expByte: validIKEAUTHByte, + }, + { + description: "INFORMATIONAL encode", + ikeMsg: validInformation, + expErr: false, + expByte: validInformationByte, + }, + { + description: "Create_Child_SA encode", + ikeMsg: validCreateChildSA, + expErr: false, + expByte: validCreateChildSAByte, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.ikeMsg.Encode() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expByte, result) + } + }) + } +} diff --git a/message/payload_authentication.go b/message/payload_authentication.go new file mode 100644 index 0000000..e112381 --- /dev/null +++ b/message/payload_authentication.go @@ -0,0 +1,33 @@ +package message + +import "github.com/pkg/errors" + +var _ IKEPayload = &Authentication{} + +type Authentication struct { + AuthenticationMethod uint8 + AuthenticationData []byte +} + +func (authentication *Authentication) Type() IKEPayloadType { return TypeAUTH } + +func (authentication *Authentication) marshal() ([]byte, error) { + authenticationData := make([]byte, 4) + authenticationData[0] = authentication.AuthenticationMethod + authenticationData = append(authenticationData, authentication.AuthenticationData...) + return authenticationData, nil +} + +func (authentication *Authentication) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) <= 4 { + return errors.Errorf("Authentication: No sufficient bytes to decode next authentication") + } + + authentication.AuthenticationMethod = b[0] + authentication.AuthenticationData = append(authentication.AuthenticationData, b[4:]...) + } + + return nil +} diff --git a/message/payload_authentication_test.go b/message/payload_authentication_test.go new file mode 100644 index 0000000..ffec7fa --- /dev/null +++ b/message/payload_authentication_test.go @@ -0,0 +1,81 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validAuthentication = Authentication{ + AuthenticationMethod: SharedKeyMesageIntegrityCode, + AuthenticationData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + } + + validAuthenticationByte = []byte{ + 0x02, 0x00, 0x00, 0x00, 0x7d, 0x09, 0x18, 0x42, + 0x60, 0x9c, 0x9e, 0x20, 0x56, 0x9f, 0xc0, 0x39, + 0xda, 0x3f, 0x22, 0x2a, 0xb8, 0x56, 0x81, 0x8a, + } +) + +func TestAuthenticationMarshal(t *testing.T) { + testcases := []struct { + description string + authentication Authentication + expMarshal []byte + }{ + { + description: "Authentication marshal", + authentication: validAuthentication, + expMarshal: validAuthenticationByte, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.authentication.marshal() + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + }) + } +} + +func TestAuthenticationUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expErr bool + expMarshal Authentication + }{ + { + description: "No sufficient bytes to decode next Authentication", + b: []byte{ + 0x01, 0x02, 0x03, 0x04, + }, + expErr: true, + }, + { + description: "Authentication Unmarshal", + b: validAuthenticationByte, + expMarshal: validAuthentication, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var authentication Authentication + err := authentication.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, authentication) + } + }) + } +} diff --git a/message/payload_certificate.go b/message/payload_certificate.go new file mode 100644 index 0000000..1cd891e --- /dev/null +++ b/message/payload_certificate.go @@ -0,0 +1,33 @@ +package message + +import "github.com/pkg/errors" + +var _ IKEPayload = &Certificate{} + +type Certificate struct { + CertificateEncoding uint8 + CertificateData []byte +} + +func (certificate *Certificate) Type() IKEPayloadType { return TypeCERT } + +func (certificate *Certificate) marshal() ([]byte, error) { + certificateData := make([]byte, 1) + certificateData[0] = certificate.CertificateEncoding + certificateData = append(certificateData, certificate.CertificateData...) + return certificateData, nil +} + +func (certificate *Certificate) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) <= 1 { + return errors.Errorf("Certificate: No sufficient bytes to decode next certificate") + } + + certificate.CertificateEncoding = b[0] + certificate.CertificateData = append(certificate.CertificateData, b[1:]...) + } + + return nil +} diff --git a/message/payload_certificate_test.go b/message/payload_certificate_test.go new file mode 100644 index 0000000..07ccd70 --- /dev/null +++ b/message/payload_certificate_test.go @@ -0,0 +1,81 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validCertificate = Certificate{ + CertificateEncoding: ID_FQDN, + CertificateData: []byte{ + 0x6e, 0x33, 0x69, 0x77, 0x66, 0x2e, 0x73, 0x61, + 0x76, 0x69, 0x61, 0x68, 0x35, 0x67, 0x63, 0x2e, + 0x6f, 0x72, 0x67, + }, + } + + validCertificateByte = []byte{ + 0x02, 0x6e, 0x33, 0x69, 0x77, 0x66, 0x2e, 0x73, + 0x61, 0x76, 0x69, 0x61, 0x68, 0x35, 0x67, 0x63, + 0x2e, 0x6f, 0x72, 0x67, + } +) + +func TestCertificateMarshal(t *testing.T) { + testcases := []struct { + description string + crt Certificate + expMarshal []byte + }{ + { + description: "Certificate marshal", + crt: validCertificate, + expMarshal: validCertificateByte, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.crt.marshal() + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + }) + } +} + +func TestCertificateUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expErr bool + expMarshal Certificate + }{ + { + description: "No sufficient bytes to decode next certificate", + b: []byte{ + 0x01, + }, + expErr: true, + }, + { + description: "Certificate Unmarshal", + b: validCertificateByte, + expMarshal: validCertificate, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var crt Certificate + err := crt.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, crt) + } + }) + } +} diff --git a/message/payload_certificaterequest.go b/message/payload_certificaterequest.go new file mode 100644 index 0000000..23b0c11 --- /dev/null +++ b/message/payload_certificaterequest.go @@ -0,0 +1,33 @@ +package message + +import "github.com/pkg/errors" + +var _ IKEPayload = &CertificateRequest{} + +type CertificateRequest struct { + CertificateEncoding uint8 + CertificationAuthority []byte +} + +func (certificateRequest *CertificateRequest) Type() IKEPayloadType { return TypeCERTreq } + +func (certificateRequest *CertificateRequest) marshal() ([]byte, error) { + certificateRequestData := make([]byte, 1) + certificateRequestData[0] = certificateRequest.CertificateEncoding + certificateRequestData = append(certificateRequestData, certificateRequest.CertificationAuthority...) + return certificateRequestData, nil +} + +func (certificateRequest *CertificateRequest) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) <= 1 { + return errors.Errorf("CertificateRequest: No sufficient bytes to decode next certificate request") + } + + certificateRequest.CertificateEncoding = b[0] + certificateRequest.CertificationAuthority = append(certificateRequest.CertificationAuthority, b[1:]...) + } + + return nil +} diff --git a/message/payload_certificaterequest_test.go b/message/payload_certificaterequest_test.go new file mode 100644 index 0000000..409802b --- /dev/null +++ b/message/payload_certificaterequest_test.go @@ -0,0 +1,81 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validCertificateRequest = CertificateRequest{ + CertificateEncoding: ID_FQDN, + CertificationAuthority: []byte{ + 0x6e, 0x33, 0x69, 0x77, 0x66, 0x2e, 0x73, 0x61, + 0x76, 0x69, 0x61, 0x68, 0x35, 0x67, 0x63, 0x2e, + 0x6f, 0x72, 0x67, + }, + } + + validCertificateRequestByte = []byte{ + 0x02, 0x6e, 0x33, 0x69, 0x77, 0x66, 0x2e, 0x73, + 0x61, 0x76, 0x69, 0x61, 0x68, 0x35, 0x67, 0x63, + 0x2e, 0x6f, 0x72, 0x67, + } +) + +func TestCertificateRequestMarshal(t *testing.T) { + testcases := []struct { + description string + crt CertificateRequest + expMarshal []byte + }{ + { + description: "CertificateRequest marshal", + crt: validCertificateRequest, + expMarshal: validCertificateRequestByte, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.crt.marshal() + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + }) + } +} + +func TestCertificateRequestUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expErr bool + expMarshal CertificateRequest + }{ + { + description: "No sufficient bytes to decode next certificate request", + b: []byte{ + 0x01, + }, + expErr: true, + }, + { + description: "CertificateRequest Unmarshal", + b: validCertificateRequestByte, + expMarshal: validCertificateRequest, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var crt CertificateRequest + err := crt.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, crt) + } + }) + } +} diff --git a/message/payload_configuration.go b/message/payload_configuration.go new file mode 100644 index 0000000..f31041e --- /dev/null +++ b/message/payload_configuration.go @@ -0,0 +1,80 @@ +package message + +import ( + "encoding/binary" + + "github.com/pkg/errors" +) + +var _ IKEPayload = &Configuration{} + +type Configuration struct { + ConfigurationType uint8 + ConfigurationAttribute ConfigurationAttributeContainer +} + +type ConfigurationAttributeContainer []*IndividualConfigurationAttribute + +type IndividualConfigurationAttribute struct { + Type uint16 + Value []byte +} + +func (configuration *Configuration) Type() IKEPayloadType { return TypeCP } + +func (configuration *Configuration) marshal() ([]byte, error) { + configurationData := make([]byte, 4) + configurationData[0] = configuration.ConfigurationType + + for _, attribute := range configuration.ConfigurationAttribute { + individualConfigurationAttributeData := make([]byte, 4) + + binary.BigEndian.PutUint16(individualConfigurationAttributeData[0:2], (attribute.Type & 0x7fff)) + attributeLen := len(attribute.Value) + if attributeLen > 0xFFFF { + return nil, errors.Errorf("Configuration: attribute value length exceeds uint16 limit: %d", attributeLen) + } + binary.BigEndian.PutUint16(individualConfigurationAttributeData[2:4], uint16(attributeLen)) + + individualConfigurationAttributeData = append(individualConfigurationAttributeData, attribute.Value...) + + configurationData = append(configurationData, individualConfigurationAttributeData...) + } + return configurationData, nil +} + +func (configuration *Configuration) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) <= 4 { + return errors.Errorf("Configuration: No sufficient bytes to decode next configuration") + } + configuration.ConfigurationType = b[0] + + configurationAttributeData := b[4:] + + for len(configurationAttributeData) > 0 { + // bounds checking + if len(configurationAttributeData) < 4 { + return errors.Errorf("ConfigurationAttribute: No sufficient bytes to decode next configuration attribute") + } + length := binary.BigEndian.Uint16(configurationAttributeData[2:4]) + if len(configurationAttributeData) < int(4+length) { + return errors.Errorf("ConfigurationAttribute: TLV attribute length error") + } + + individualConfigurationAttribute := new(IndividualConfigurationAttribute) + + individualConfigurationAttribute.Type = binary.BigEndian.Uint16(configurationAttributeData[0:2]) + configurationAttributeData = configurationAttributeData[4:] + individualConfigurationAttribute.Value = append( + individualConfigurationAttribute.Value, + configurationAttributeData[:length]...) + configurationAttributeData = configurationAttributeData[length:] + + configuration.ConfigurationAttribute = append(configuration.ConfigurationAttribute, individualConfigurationAttribute) + } + } + + return nil +} diff --git a/message/payload_delete.go b/message/payload_delete.go new file mode 100644 index 0000000..9e3771c --- /dev/null +++ b/message/payload_delete.go @@ -0,0 +1,58 @@ +package message + +import ( + "encoding/binary" + + "github.com/pkg/errors" +) + +var _ IKEPayload = &Delete{} + +type Delete struct { + ProtocolID uint8 + SPISize uint8 + NumberOfSPI uint16 + SPIs []byte +} + +func (d *Delete) Type() IKEPayloadType { return TypeD } + +func (d *Delete) marshal() ([]byte, error) { + if len(d.SPIs) != (int(d.SPISize) * int(d.NumberOfSPI)) { + return nil, errors.Errorf("Total bytes of all SPIs not correct") + } + + deleteData := make([]byte, 4) + + deleteData[0] = d.ProtocolID + deleteData[1] = d.SPISize + binary.BigEndian.PutUint16(deleteData[2:4], d.NumberOfSPI) + + if int(d.NumberOfSPI) > 0 { + deleteData = append(deleteData, d.SPIs...) + } + + return deleteData, nil +} + +func (d *Delete) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) <= 3 { + return errors.Errorf("Delete: No sufficient bytes to decode next delete") + } + spiSize := b[1] + numberOfSPI := binary.BigEndian.Uint16(b[2:4]) + if len(b) < (4 + (int(spiSize) * int(numberOfSPI))) { + return errors.Errorf("Delete: No Sufficient bytes to get SPIs according to the length specified in header") + } + + d.ProtocolID = b[0] + d.SPISize = spiSize + d.NumberOfSPI = numberOfSPI + + d.SPIs = append(d.SPIs, b[4:]...) + } + + return nil +} diff --git a/message/payload_delete_test.go b/message/payload_delete_test.go new file mode 100644 index 0000000..43e3c5e --- /dev/null +++ b/message/payload_delete_test.go @@ -0,0 +1,111 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDeleteMarshal(t *testing.T) { + testcases := []struct { + description string + delete Delete + expMarshal []byte + expErr bool + }{ + { + description: "Total bytes of all SPIs not correct", + delete: Delete{ + ProtocolID: TypeESP, + SPISize: 4, + NumberOfSPI: 1, + SPIs: []byte{0x01, 0x02, 0x03}, + }, + expErr: true, + }, + { + description: "Delete marshal TypeIKE", + delete: Delete{ + ProtocolID: TypeIKE, + SPISize: 0, + NumberOfSPI: 0, + SPIs: nil, + }, + expMarshal: []byte{ + 0x01, 0x00, 0x00, 0x00, + }, + expErr: false, + }, + { + description: "Delete marshal TypeESP", + delete: Delete{ + ProtocolID: TypeESP, + SPISize: 4, + NumberOfSPI: 1, + SPIs: []byte{0x01, 0x02, 0x03, 0x04}, + }, + expMarshal: []byte{ + 0x03, 0x04, 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, + }, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.delete.marshal() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + } + }) + } +} + +func TestDeleteUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expErr bool + expMarshal Delete + }{ + { + description: "No sufficient bytes to decode next delete", + b: []byte{0x01, 0x02, 0x03}, + expErr: true, + }, + { + description: "No Sufficient bytes to get SPIs according to the length specified in header", + b: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, + expErr: true, + }, + { + description: "Delete Unmarshal", + b: []byte{ + 0x03, 0x04, 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, + }, + expMarshal: Delete{ + ProtocolID: TypeESP, + SPISize: 4, + NumberOfSPI: 1, + SPIs: []byte{0x01, 0x02, 0x03, 0x04}, + }, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var d Delete + err := d.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, d) + } + }) + } +} diff --git a/message/payload_eap.go b/message/payload_eap.go new file mode 100644 index 0000000..d65f01b --- /dev/null +++ b/message/payload_eap.go @@ -0,0 +1,100 @@ +package message + +import ( + "encoding/binary" + + "github.com/pkg/errors" +) + +var _ IKEPayload = &EAP{} + +type EAP struct { + Code uint8 + Identifier uint8 + EAPTypeData EAPTypeDataContainer +} + +type EAPTypeDataContainer []EAPTypeFormat + +type EAPTypeFormat interface { + // Type specifies EAP types + Type() EAPType + + // Called by EAP.marshal() or EAP.unmarshal() + marshal() ([]byte, error) + unmarshal(b []byte) error +} + +func (eap *EAP) Type() IKEPayloadType { return TypeEAP } + +func (eap *EAP) marshal() ([]byte, error) { + eapData := make([]byte, 4) + + eapData[0] = eap.Code + eapData[1] = eap.Identifier + + if len(eap.EAPTypeData) > 0 { + eapTypeData, err := eap.EAPTypeData[0].marshal() + if err != nil { + return nil, errors.Errorf("EAP: EAP type data marshal failed: %+v", err) + } + + eapData = append(eapData, eapTypeData...) + } + + eapDataLen := len(eapData) + if eapDataLen > 0xFFFF { + return nil, errors.Errorf("EAP: eapData length exceeds uint16 limit: %d", eapDataLen) + } + binary.BigEndian.PutUint16(eapData[2:4], uint16(eapDataLen)) + return eapData, nil +} + +func (eap *EAP) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) < 4 { + return errors.Errorf("EAP: No sufficient bytes to decode next EAP payload") + } + eapPayloadLength := binary.BigEndian.Uint16(b[2:4]) + if eapPayloadLength < 4 { + return errors.Errorf("EAP: Payload length specified in the header is too small for EAP") + } + if len(b) != int(eapPayloadLength) { + return errors.Errorf("EAP: Received payload length not matches the length specified in header") + } + + eap.Code = b[0] + eap.Identifier = b[1] + + // EAP Success or Failed + if eapPayloadLength == 4 { + return nil + } + + eapType := b[4] + var eapTypeData EAPTypeFormat + + switch EAPType(eapType) { + case EAPTypeIdentity: + eapTypeData = new(EAPIdentity) + case EAPTypeNotification: + eapTypeData = new(EAPNotification) + case EAPTypeNak: + eapTypeData = new(EAPNak) + case EAPTypeExpanded: + eapTypeData = new(EAPExpanded) + default: + // TODO: Create unsupprted type to handle it + return errors.Errorf("EAP: Not supported EAP type") + } + + if err := eapTypeData.unmarshal(b[4:]); err != nil { + return errors.Errorf("EAP: Unamrshal EAP type data failed: %+v", err) + } + + eap.EAPTypeData = append(eap.EAPTypeData, eapTypeData) + } + + return nil +} diff --git a/message/payload_eap_test.go b/message/payload_eap_test.go new file mode 100644 index 0000000..fd7ce4b --- /dev/null +++ b/message/payload_eap_test.go @@ -0,0 +1,249 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + eapIdentity = EAP{ + Code: EAPCodeRequest, + Identifier: 9, + EAPTypeData: EAPTypeDataContainer{ + &EAPIdentity{ + IdentityData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + }, + }, + } + + eapIdentityByte = []byte{ + 0x01, 0x09, 0x00, 0x19, 0x01, 0x7d, 0x09, + 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, 0x56, + 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + } + + eapNotification = EAP{ + Code: EAPCodeRequest, + Identifier: 9, + EAPTypeData: EAPTypeDataContainer{ + &EAPNotification{ + NotificationData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + }, + }, + } + + eapNotificationByte = []byte{ + 0x01, 0x09, 0x00, 0x19, 0x02, 0x7d, 0x09, 0x18, + 0x42, 0x60, 0x9c, 0x9e, 0x20, 0x56, 0x9f, 0xc0, + 0x39, 0xda, 0x3f, 0x22, 0x2a, 0xb8, 0x56, 0x81, + 0x8a, + } + + eapNak = EAP{ + Code: EAPCodeRequest, + Identifier: 9, + EAPTypeData: EAPTypeDataContainer{ + &EAPNak{ + NakData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + }, + }, + } + + eapNakByte = []byte{ + 0x01, 0x09, 0x00, 0x19, 0x03, 0x7d, 0x09, 0x18, + 0x42, 0x60, 0x9c, 0x9e, 0x20, 0x56, 0x9f, 0xc0, + 0x39, 0xda, 0x3f, 0x22, 0x2a, 0xb8, 0x56, 0x81, + 0x8a, + } + + eapExpanded = EAP{ + Code: EAPCodeRequest, + Identifier: 9, + EAPTypeData: EAPTypeDataContainer{ + &EAPExpanded{ + VendorID: VendorID3GPP, + VendorType: VendorTypeEAP5G, + VendorData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + }, + }, + } + + eapExpandedByte = []byte{ + 0x01, 0x09, 0x00, 0x20, 0xfe, 0x00, 0x28, 0xaf, + 0x00, 0x00, 0x00, 0x03, 0x7d, 0x09, 0x18, 0x42, + 0x60, 0x9c, 0x9e, 0x20, 0x56, 0x9f, 0xc0, 0x39, + 0xda, 0x3f, 0x22, 0x2a, 0xb8, 0x56, 0x81, 0x8a, + } +) + +func TestEAPMarshal(t *testing.T) { + testcases := []struct { + description string + eap EAP + expMarshal []byte + expErr bool + }{ + { + description: "EAP identity is empty", + eap: EAP{ + Code: EAPCodeRequest, + Identifier: 9, + EAPTypeData: EAPTypeDataContainer{ + &EAPIdentity{ + IdentityData: nil, + }, + }, + }, + expErr: true, + }, + { + description: "EAPIdentity marshal", + eap: eapIdentity, + expMarshal: eapIdentityByte, + expErr: false, + }, + { + description: "EAP notification is empty", + eap: EAP{ + Code: EAPCodeRequest, + Identifier: 9, + EAPTypeData: EAPTypeDataContainer{ + &EAPNotification{ + NotificationData: nil, + }, + }, + }, + expErr: true, + }, + { + description: "EAPNotification marshal", + eap: eapNotification, + expMarshal: eapNotificationByte, + expErr: false, + }, + { + description: "EAP nak is empty", + eap: EAP{ + Code: EAPCodeRequest, + Identifier: 9, + EAPTypeData: EAPTypeDataContainer{ + &EAPNak{ + NakData: nil, + }, + }, + }, + expErr: true, + }, + { + description: "EAPNak marshal", + eap: eapNak, + expMarshal: eapNakByte, + expErr: false, + }, + { + description: "EAPExpanded marshal", + eap: eapExpanded, + expMarshal: eapExpandedByte, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.eap.marshal() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + } + }) + } +} + +func TestEAPUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expMarshal EAP + expErr bool + }{ + { + description: "No sufficient bytes to decode next EAP payload", + b: []byte{0x01, 0x02, 0x03}, + expErr: true, + }, + { + description: "Payload length specified in the header is too small for EAP", + b: []byte{0x01, 0x02, 0x00, 0x03}, + expErr: true, + }, + { + description: "Received payload length not matches the length specified in header", + b: []byte{0x01, 0x02, 0x00, 0x07, 0x01}, + expErr: true, + }, + { + description: "EAPIdentity unmarshal", + b: eapIdentityByte, + expMarshal: eapIdentity, + expErr: false, + }, + { + description: "EAPNotification unmarshal", + b: eapNotificationByte, + expMarshal: eapNotification, + expErr: false, + }, + { + description: "EAPNak unmarshal", + b: eapNakByte, + expMarshal: eapNak, + expErr: false, + }, + { + description: "EAPExpanded: No sufficient bytes to decode the EAP expanded type", + b: []byte{ + 0x01, 0x09, 0x00, 0x20, 0xfe, 0x00, 0x28, + }, + expErr: true, + }, + { + description: "EAPExpanded unmarshal", + b: eapExpandedByte, + expMarshal: eapExpanded, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var eap EAP + err := eap.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, eap) + } + }) + } +} diff --git a/message/payload_eapexpanded.go b/message/payload_eapexpanded.go new file mode 100644 index 0000000..af90298 --- /dev/null +++ b/message/payload_eapexpanded.go @@ -0,0 +1,52 @@ +package message + +import ( + "encoding/binary" + + "github.com/pkg/errors" +) + +var _ EAPTypeFormat = &EAPExpanded{} + +type EAPExpanded struct { + VendorID uint32 + VendorType uint32 + VendorData []byte +} + +func (eapExpanded *EAPExpanded) Type() EAPType { return EAPTypeExpanded } + +func (eapExpanded *EAPExpanded) marshal() ([]byte, error) { + eapExpandedData := make([]byte, 8) + + vendorID := eapExpanded.VendorID & 0x00ffffff + typeAndVendorID := (uint32(EAPTypeExpanded)<<24 | vendorID) + + binary.BigEndian.PutUint32(eapExpandedData[0:4], typeAndVendorID) + binary.BigEndian.PutUint32(eapExpandedData[4:8], eapExpanded.VendorType) + + if len(eapExpanded.VendorData) == 0 { + return eapExpandedData, nil + } + + eapExpandedData = append(eapExpandedData, eapExpanded.VendorData...) + return eapExpandedData, nil +} + +func (eapExpanded *EAPExpanded) unmarshal(b []byte) error { + if len(b) > 0 { + if len(b) < 8 { + return errors.Errorf("EAPExpanded: No sufficient bytes to decode the EAP expanded type") + } + + typeAndVendorID := binary.BigEndian.Uint32(b[0:4]) + eapExpanded.VendorID = typeAndVendorID & 0x00ffffff + + eapExpanded.VendorType = binary.BigEndian.Uint32(b[4:8]) + + if len(b) > 8 { + eapExpanded.VendorData = append(eapExpanded.VendorData, b[8:]...) + } + } + return nil +} diff --git a/message/payload_eapexpanded_test.go b/message/payload_eapexpanded_test.go new file mode 100644 index 0000000..8d4ee71 --- /dev/null +++ b/message/payload_eapexpanded_test.go @@ -0,0 +1,90 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validEAPExpanded = EAPExpanded{ + VendorID: VendorID3GPP, + VendorType: VendorTypeEAP5G, + VendorData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + } + + validEAPExpandedByte = []byte{ + 0xfe, 0x00, 0x28, 0xaf, 0x00, 0x00, 0x00, 0x03, + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + } +) + +func TestEAPExpandedMarshal(t *testing.T) { + testcases := []struct { + description string + eap EAPExpanded + expMarshal []byte + expErr bool + }{ + { + description: "EAPExpanded marshal", + eap: validEAPExpanded, + expMarshal: validEAPExpandedByte, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.eap.marshal() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + } + }) + } +} + +func TestEAPExpandedUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expMarshal EAPExpanded + expErr bool + }{ + { + description: "No sufficient bytes to decode the EAP expanded type", + b: []byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + }, + expErr: true, + }, + { + description: "EAPExpanded Unmarshal", + b: validEAPExpandedByte, + expMarshal: validEAPExpanded, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var eap EAPExpanded + err := eap.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, eap) + } + }) + } +} diff --git a/message/payload_eapidentity.go b/message/payload_eapidentity.go new file mode 100644 index 0000000..6e89e64 --- /dev/null +++ b/message/payload_eapidentity.go @@ -0,0 +1,28 @@ +package message + +import "github.com/pkg/errors" + +var _ EAPTypeFormat = &EAPIdentity{} + +type EAPIdentity struct { + IdentityData []byte +} + +func (eapIdentity *EAPIdentity) Type() EAPType { return EAPTypeIdentity } + +func (eapIdentity *EAPIdentity) marshal() ([]byte, error) { + if len(eapIdentity.IdentityData) == 0 { + return nil, errors.Errorf("EAPIdentity: EAP identity is empty") + } + + eapIdentityData := []byte{byte(EAPTypeIdentity)} + eapIdentityData = append(eapIdentityData, eapIdentity.IdentityData...) + return eapIdentityData, nil +} + +func (eapIdentity *EAPIdentity) unmarshal(b []byte) error { + if len(b) > 1 { + eapIdentity.IdentityData = append(eapIdentity.IdentityData, b[1:]...) + } + return nil +} diff --git a/message/payload_eapidentity_test.go b/message/payload_eapidentity_test.go new file mode 100644 index 0000000..f8ae802 --- /dev/null +++ b/message/payload_eapidentity_test.go @@ -0,0 +1,81 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validEAPIdentity = EAPIdentity{ + IdentityData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + } + + validEAPIdentityByte = []byte{ + 0x01, 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, + 0x20, 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, + 0x2a, 0xb8, 0x56, 0x81, 0x8a, + } +) + +func TestEAPIdentityMarshal(t *testing.T) { + testcases := []struct { + description string + eap EAPIdentity + expMarshal []byte + expErr bool + }{ + { + description: "EAP identity is empty", + eap: EAPIdentity{ + IdentityData: nil, + }, + expErr: true, + }, + { + description: "EAPIdentity marshal", + eap: validEAPIdentity, + expMarshal: validEAPIdentityByte, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.eap.marshal() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + } + }) + } +} + +func TestEAPIdentityUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expMarshal EAPIdentity + }{ + { + description: "EAPIdentity Unmarshal", + b: validEAPIdentityByte, + expMarshal: validEAPIdentity, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var eap EAPIdentity + err := eap.unmarshal(tc.b) + require.NoError(t, err) + require.Equal(t, tc.expMarshal, eap) + }) + } +} diff --git a/message/payload_eapnak.go b/message/payload_eapnak.go new file mode 100644 index 0000000..5e5441b --- /dev/null +++ b/message/payload_eapnak.go @@ -0,0 +1,28 @@ +package message + +import "github.com/pkg/errors" + +var _ EAPTypeFormat = &EAPNak{} + +type EAPNak struct { + NakData []byte +} + +func (eapNak *EAPNak) Type() EAPType { return EAPTypeNak } + +func (eapNak *EAPNak) marshal() ([]byte, error) { + if len(eapNak.NakData) == 0 { + return nil, errors.Errorf("EAPNak: EAP nak is empty") + } + + eapNakData := []byte{byte(EAPTypeNak)} + eapNakData = append(eapNakData, eapNak.NakData...) + return eapNakData, nil +} + +func (eapNak *EAPNak) unmarshal(b []byte) error { + if len(b) > 1 { + eapNak.NakData = append(eapNak.NakData, b[1:]...) + } + return nil +} diff --git a/message/payload_eapnak_test.go b/message/payload_eapnak_test.go new file mode 100644 index 0000000..94065a7 --- /dev/null +++ b/message/payload_eapnak_test.go @@ -0,0 +1,81 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validEAPNak = EAPNak{ + NakData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + } + + validEAPNakByte = []byte{ + 0x03, 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, + 0x20, 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, + 0x2a, 0xb8, 0x56, 0x81, 0x8a, + } +) + +func TestEAPNakMarshal(t *testing.T) { + testcases := []struct { + description string + eap EAPNak + expMarshal []byte + expErr bool + }{ + { + description: "EAP nak is empty", + eap: EAPNak{ + NakData: nil, + }, + expErr: true, + }, + { + description: "EAPNak marshal", + eap: validEAPNak, + expMarshal: validEAPNakByte, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.eap.marshal() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + } + }) + } +} + +func TestEAPNakUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expMarshal EAPNak + }{ + { + description: "EAPNak Unmarshal", + b: validEAPNakByte, + expMarshal: validEAPNak, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var eap EAPNak + err := eap.unmarshal(tc.b) + require.NoError(t, err) + require.Equal(t, tc.expMarshal, eap) + }) + } +} diff --git a/message/payload_eapnotification.go b/message/payload_eapnotification.go new file mode 100644 index 0000000..f884438 --- /dev/null +++ b/message/payload_eapnotification.go @@ -0,0 +1,28 @@ +package message + +import "github.com/pkg/errors" + +var _ EAPTypeFormat = &EAPNotification{} + +type EAPNotification struct { + NotificationData []byte +} + +func (eapNotification *EAPNotification) Type() EAPType { return EAPTypeNotification } + +func (eapNotification *EAPNotification) marshal() ([]byte, error) { + if len(eapNotification.NotificationData) == 0 { + return nil, errors.Errorf("EAPNotification: EAP notification is empty") + } + + eapNotificationData := []byte{byte(EAPTypeNotification)} + eapNotificationData = append(eapNotificationData, eapNotification.NotificationData...) + return eapNotificationData, nil +} + +func (eapNotification *EAPNotification) unmarshal(b []byte) error { + if len(b) > 1 { + eapNotification.NotificationData = append(eapNotification.NotificationData, b[1:]...) + } + return nil +} diff --git a/message/payload_eapnotification_test.go b/message/payload_eapnotification_test.go new file mode 100644 index 0000000..afe5d31 --- /dev/null +++ b/message/payload_eapnotification_test.go @@ -0,0 +1,81 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validEAPNotification = EAPNotification{ + NotificationData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + } + + validEAPNotificationByte = []byte{ + 0x02, 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, + 0x20, 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, + 0x2a, 0xb8, 0x56, 0x81, 0x8a, + } +) + +func TestEAPNotificationMarshal(t *testing.T) { + testcases := []struct { + description string + eap EAPNotification + expMarshal []byte + expErr bool + }{ + { + description: "EAP notification is empty", + eap: EAPNotification{ + NotificationData: nil, + }, + expErr: true, + }, + { + description: "EAPNotification marshal", + eap: validEAPNotification, + expMarshal: validEAPNotificationByte, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.eap.marshal() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + } + }) + } +} + +func TestEAPNotificationUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expMarshal EAPNotification + }{ + { + description: "EAPNotification Unmarshal", + b: validEAPNotificationByte, + expMarshal: validEAPNotification, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var eap EAPNotification + err := eap.unmarshal(tc.b) + require.NoError(t, err) + require.Equal(t, tc.expMarshal, eap) + }) + } +} diff --git a/message/payload_encrypted.go b/message/payload_encrypted.go new file mode 100644 index 0000000..031f628 --- /dev/null +++ b/message/payload_encrypted.go @@ -0,0 +1,25 @@ +package message + +import "github.com/pkg/errors" + +var _ IKEPayload = &Encrypted{} + +type Encrypted struct { + NextPayload uint8 + EncryptedData []byte +} + +func (encrypted *Encrypted) Type() IKEPayloadType { return TypeSK } + +func (encrypted *Encrypted) marshal() ([]byte, error) { + if len(encrypted.EncryptedData) == 0 { + return nil, errors.Errorf("[Encrypted] The encrypted data is empty") + } + + return encrypted.EncryptedData, nil +} + +func (encrypted *Encrypted) unmarshal(b []byte) error { + encrypted.EncryptedData = append(encrypted.EncryptedData, b...) + return nil +} diff --git a/message/payload_encrypted_test.go b/message/payload_encrypted_test.go new file mode 100644 index 0000000..82a3fa8 --- /dev/null +++ b/message/payload_encrypted_test.go @@ -0,0 +1,81 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validEncrypted = Encrypted{ + EncryptedData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + } + + validEncryptedByte = []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + } +) + +func TestEncryptedMarshal(t *testing.T) { + testcases := []struct { + description string + encrypted Encrypted + expMarshal []byte + expErr bool + }{ + { + description: "The encrypted data is empty", + encrypted: Encrypted{ + EncryptedData: nil, + }, + expErr: true, + }, + { + description: "Encrypted marshal", + encrypted: validEncrypted, + expMarshal: validEncryptedByte, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.encrypted.marshal() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + } + }) + } +} + +func TestEncryptedUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expMarshal Encrypted + }{ + { + description: "Encrypted Unmarshal", + b: validEncryptedByte, + expMarshal: validEncrypted, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var encrypted Encrypted + err := encrypted.unmarshal(tc.b) + require.NoError(t, err) + require.Equal(t, tc.expMarshal, encrypted) + }) + } +} diff --git a/message/payload_identificationinitiator.go b/message/payload_identificationinitiator.go new file mode 100644 index 0000000..3d4f539 --- /dev/null +++ b/message/payload_identificationinitiator.go @@ -0,0 +1,33 @@ +package message + +import "github.com/pkg/errors" + +var _ IKEPayload = &IdentificationInitiator{} + +type IdentificationInitiator struct { + IDType uint8 + IDData []byte +} + +func (identification *IdentificationInitiator) Type() IKEPayloadType { return TypeIDi } + +func (identification *IdentificationInitiator) marshal() ([]byte, error) { + identificationData := make([]byte, 4) + identificationData[0] = identification.IDType + identificationData = append(identificationData, identification.IDData...) + return identificationData, nil +} + +func (identification *IdentificationInitiator) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) <= 4 { + return errors.Errorf("Identification: No sufficient bytes to decode next identification") + } + + identification.IDType = b[0] + identification.IDData = append(identification.IDData, b[4:]...) + } + + return nil +} diff --git a/message/payload_identificationinitiator_test.go b/message/payload_identificationinitiator_test.go new file mode 100644 index 0000000..37bd4d5 --- /dev/null +++ b/message/payload_identificationinitiator_test.go @@ -0,0 +1,78 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIdentificationInitiatorMarshal(t *testing.T) { + testcases := []struct { + description string + id IdentificationInitiator + expMarshal []byte + }{ + { + description: "IdentificationInitiator marshal", + id: IdentificationInitiator{ + IDType: ID_KEY_ID, + IDData: []byte{ + 0x55, 0x45, + }, + }, + expMarshal: []byte{ + 0xb, 0x0, 0x0, 0x0, 0x55, 0x45, + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.id.marshal() + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + }) + } +} + +func TestIdentificationInitiatorUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expErr bool + expMarshal IdentificationInitiator + }{ + { + description: "No sufficient bytes to decode next identification", + b: []byte{ + 0x01, 0x02, 0x03, + }, + expErr: true, + }, + { + description: "IdentificationInitiator Unmarshal", + b: []byte{ + 0xb, 0x0, 0x0, 0x0, 0x55, 0x45, + }, + expMarshal: IdentificationInitiator{ + IDType: ID_KEY_ID, + IDData: []byte{ + 0x55, 0x45, + }, + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var id IdentificationInitiator + err := id.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, id) + } + }) + } +} diff --git a/message/payload_identificationresponder.go b/message/payload_identificationresponder.go new file mode 100644 index 0000000..8d675b7 --- /dev/null +++ b/message/payload_identificationresponder.go @@ -0,0 +1,33 @@ +package message + +import "github.com/pkg/errors" + +var _ IKEPayload = &IdentificationResponder{} + +type IdentificationResponder struct { + IDType uint8 + IDData []byte +} + +func (identification *IdentificationResponder) Type() IKEPayloadType { return TypeIDr } + +func (identification *IdentificationResponder) marshal() ([]byte, error) { + identificationData := make([]byte, 4) + identificationData[0] = identification.IDType + identificationData = append(identificationData, identification.IDData...) + return identificationData, nil +} + +func (identification *IdentificationResponder) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) <= 4 { + return errors.Errorf("Identification: No sufficient bytes to decode next identification") + } + + identification.IDType = b[0] + identification.IDData = append(identification.IDData, b[4:]...) + } + + return nil +} diff --git a/message/payload_identificationresponder_test.go b/message/payload_identificationresponder_test.go new file mode 100644 index 0000000..f6280b2 --- /dev/null +++ b/message/payload_identificationresponder_test.go @@ -0,0 +1,78 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIdentificationResponderMarshal(t *testing.T) { + testcases := []struct { + description string + id IdentificationResponder + expMarshal []byte + }{ + { + description: "IdentificationResponder marshal", + id: IdentificationResponder{ + IDType: ID_KEY_ID, + IDData: []byte{ + 0x55, 0x45, + }, + }, + expMarshal: []byte{ + 0xb, 0x0, 0x0, 0x0, 0x55, 0x45, + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.id.marshal() + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + }) + } +} + +func TestIdentificationResponderUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expErr bool + expMarshal IdentificationResponder + }{ + { + description: "No sufficient bytes to decode next identification", + b: []byte{ + 0x01, 0x02, 0x03, + }, + expErr: true, + }, + { + description: "IdentificationResponder Unmarshal", + b: []byte{ + 0xb, 0x0, 0x0, 0x0, 0x55, 0x45, + }, + expMarshal: IdentificationResponder{ + IDType: ID_KEY_ID, + IDData: []byte{ + 0x55, 0x45, + }, + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var id IdentificationResponder + err := id.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, id) + } + }) + } +} diff --git a/message/payload_keyexchange.go b/message/payload_keyexchange.go new file mode 100644 index 0000000..44f7e9f --- /dev/null +++ b/message/payload_keyexchange.go @@ -0,0 +1,37 @@ +package message + +import ( + "encoding/binary" + + "github.com/pkg/errors" +) + +var _ IKEPayload = &KeyExchange{} + +type KeyExchange struct { + DiffieHellmanGroup uint16 + KeyExchangeData []byte +} + +func (keyExchange *KeyExchange) Type() IKEPayloadType { return TypeKE } + +func (keyExchange *KeyExchange) marshal() ([]byte, error) { + keyExchangeData := make([]byte, 4) + binary.BigEndian.PutUint16(keyExchangeData[0:2], keyExchange.DiffieHellmanGroup) + keyExchangeData = append(keyExchangeData, keyExchange.KeyExchangeData...) + return keyExchangeData, nil +} + +func (keyExchange *KeyExchange) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) <= 4 { + return errors.Errorf("KeyExchange: No sufficient bytes to decode next key exchange data") + } + + keyExchange.DiffieHellmanGroup = binary.BigEndian.Uint16(b[0:2]) + keyExchange.KeyExchangeData = append(keyExchange.KeyExchangeData, b[4:]...) + } + + return nil +} diff --git a/message/payload_keyexchange_test.go b/message/payload_keyexchange_test.go new file mode 100644 index 0000000..a95542b --- /dev/null +++ b/message/payload_keyexchange_test.go @@ -0,0 +1,100 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validDH1024 = KeyExchange{ + DiffieHellmanGroup: DH_1024_BIT_MODP, + KeyExchangeData: []byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + }, + } + validDH1024Byte = []byte{ + 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + } + + validDH2048 = KeyExchange{ + DiffieHellmanGroup: DH_2048_BIT_MODP, + KeyExchangeData: []byte{ + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + }, + } + validDH2048Byte = []byte{ + 0x00, 0x0e, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, + } +) + +func TestKeyExchangeMarshal(t *testing.T) { + testcases := []struct { + description string + keyExchange KeyExchange + expMarshal []byte + }{ + { + description: "Marshal 1024 bit MODP group", + keyExchange: validDH1024, + expMarshal: validDH1024Byte, + }, + { + description: "Marshal 2048 bit MODP group", + keyExchange: validDH2048, + expMarshal: validDH2048Byte, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.keyExchange.marshal() + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + }) + } +} + +func TestKeyExchangeUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expKE KeyExchange + expErr bool + }{ + { + description: "No sufficient bytes to decode next key exchange data", + b: []byte{ + 0x01, 0x02, 0x03, + }, + expErr: true, + }, + { + description: "Unmarshal 1024 bit MODP group", + b: validDH1024Byte, + expKE: validDH1024, + expErr: false, + }, + { + description: "Unmarshal 2048 bit MODP group", + b: validDH2048Byte, + expKE: validDH2048, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var ke KeyExchange + err := ke.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expKE, ke) + } + }) + } +} diff --git a/message/payload_nonce.go b/message/payload_nonce.go new file mode 100644 index 0000000..5f0d47a --- /dev/null +++ b/message/payload_nonce.go @@ -0,0 +1,22 @@ +package message + +var _ IKEPayload = &Nonce{} + +type Nonce struct { + NonceData []byte +} + +func (nonce *Nonce) Type() IKEPayloadType { return TypeNiNr } + +func (nonce *Nonce) marshal() ([]byte, error) { + nonceData := make([]byte, 0) + nonceData = append(nonceData, nonce.NonceData...) + return nonceData, nil +} + +func (nonce *Nonce) unmarshal(b []byte) error { + if len(b) > 0 { + nonce.NonceData = append(nonce.NonceData, b...) + } + return nil +} diff --git a/message/payload_nonce_test.go b/message/payload_nonce_test.go new file mode 100644 index 0000000..798f632 --- /dev/null +++ b/message/payload_nonce_test.go @@ -0,0 +1,73 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validNonce = Nonce{ + NonceData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + } + + validNonceByte = []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + } +) + +func TestNonceMarshal(t *testing.T) { + testcases := []struct { + description string + nonce Nonce + expMarshal []byte + }{ + { + description: "Nonce marshal", + nonce: validNonce, + expMarshal: validNonceByte, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.nonce.marshal() + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + }) + } +} + +func TestNonceUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expErr bool + expMarshal Nonce + }{ + { + description: "Nonce Unmarshal", + b: validNonceByte, + expMarshal: validNonce, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var nonce Nonce + err := nonce.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, nonce) + } + }) + } +} diff --git a/message/payload_notification.go b/message/payload_notification.go new file mode 100644 index 0000000..a2aceb0 --- /dev/null +++ b/message/payload_notification.go @@ -0,0 +1,55 @@ +package message + +import ( + "encoding/binary" + + "github.com/pkg/errors" +) + +var _ IKEPayload = &Notification{} + +type Notification struct { + ProtocolID uint8 + NotifyMessageType uint16 + SPI []byte + NotificationData []byte +} + +func (notification *Notification) Type() IKEPayloadType { return TypeN } + +func (notification *Notification) marshal() ([]byte, error) { + notificationData := make([]byte, 4) + + notificationData[0] = notification.ProtocolID + numberofSPI := len(notification.SPI) + if numberofSPI > 0xFF { + return nil, errors.Errorf("Notification: Number of SPI exceeds uint8 limit: %d", numberofSPI) + } + notificationData[1] = uint8(numberofSPI) + binary.BigEndian.PutUint16(notificationData[2:4], notification.NotifyMessageType) + + notificationData = append(notificationData, notification.SPI...) + notificationData = append(notificationData, notification.NotificationData...) + return notificationData, nil +} + +func (notification *Notification) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) < 4 { + return errors.Errorf("Notification: No sufficient bytes to decode next notification") + } + spiSize := b[1] + if len(b) < int(4+spiSize) { + return errors.Errorf("Notification: No sufficient bytes to get SPI according to the length specified in header") + } + + notification.ProtocolID = b[0] + notification.NotifyMessageType = binary.BigEndian.Uint16(b[2:4]) + + notification.SPI = append(notification.SPI, b[4:4+spiSize]...) + notification.NotificationData = append(notification.NotificationData, b[4+spiSize:]...) + } + + return nil +} diff --git a/message/payload_notification_test.go b/message/payload_notification_test.go new file mode 100644 index 0000000..23fd8be --- /dev/null +++ b/message/payload_notification_test.go @@ -0,0 +1,86 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validNotification = Notification{ + ProtocolID: TypeNone, + NotifyMessageType: NAT_DETECTION_SOURCE_IP, + SPI: []byte{0x01, 0x02, 0x03}, + NotificationData: []byte{ + 0x50, 0xc4, 0xc2, 0xbe, 0x8e, 0x3f, 0xd9, 0x16, + 0x19, 0x24, 0x65, 0x0d, 0x14, 0x5d, 0x4f, 0xf6, + 0x46, 0xd8, 0x9d, 0x75, + }, + } + + validNotificationByte = []byte{ + 0x00, 0x03, 0x40, 0x04, 0x01, 0x02, 0x03, 0x50, + 0xc4, 0xc2, 0xbe, 0x8e, 0x3f, 0xd9, 0x16, 0x19, + 0x24, 0x65, 0x0d, 0x14, 0x5d, 0x4f, 0xf6, 0x46, + 0xd8, 0x9d, 0x75, + } +) + +func TestNotification(t *testing.T) { + testcasesMarshal := []struct { + description string + notification Notification + expMarshal []byte + }{ + { + description: "Notification marshal", + notification: validNotification, + expMarshal: validNotificationByte, + }, + } + + for _, tc := range testcasesMarshal { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.notification.marshal() + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + }) + } + + testcasesUnmarshal := []struct { + description string + b []byte + expErr bool + expMarshal Notification + }{ + { + description: "No sufficient bytes to decode next notification", + b: []byte{0x01, 0x02, 0x03}, + expErr: true, + }, + { + description: "No sufficient bytes to get SPI according to the length specified in header", + b: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, + expErr: true, + }, + { + description: "Notification Unmarshal", + b: validNotificationByte, + expMarshal: validNotification, + expErr: false, + }, + } + + for _, tc := range testcasesUnmarshal { + t.Run(tc.description, func(t *testing.T) { + var notification Notification + err := notification.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, notification) + } + }) + } +} diff --git a/message/payload_onfiguration_test.go b/message/payload_onfiguration_test.go new file mode 100644 index 0000000..67e9aad --- /dev/null +++ b/message/payload_onfiguration_test.go @@ -0,0 +1,96 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validConfiguration = Configuration{ + ConfigurationType: CFG_REQUEST, + ConfigurationAttribute: ConfigurationAttributeContainer{ + &IndividualConfigurationAttribute{ + Type: INTERNAL_IP4_ADDRESS, + Value: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + }, + }, + } + + validConfigurationByte = []byte{ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14, + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + } +) + +func TestConfigurationMarshal(t *testing.T) { + testcases := []struct { + description string + cfg Configuration + expMarshal []byte + }{ + { + description: "Configuration marshal", + cfg: validConfiguration, + expMarshal: validConfigurationByte, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.cfg.marshal() + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + }) + } +} + +func TestConfigurationUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expErr bool + expMarshal Configuration + }{ + { + description: "No sufficient bytes to decode next configuration", + b: []byte{0x01, 0x02, 0x03, 0x04}, + expErr: true, + }, + { + description: "No sufficient bytes to decode next configuration attribute", + b: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, + expErr: true, + }, + { + description: "TLV attribute length error", + b: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x05, 0x05, 0x05}, + expErr: true, + }, + { + description: "Configuration Unmarshal", + b: validConfigurationByte, + expMarshal: validConfiguration, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var cfg Configuration + err := cfg.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, cfg) + } + }) + } +} diff --git a/message/payload_securityassociation.go b/message/payload_securityassociation.go new file mode 100644 index 0000000..ab5ee54 --- /dev/null +++ b/message/payload_securityassociation.go @@ -0,0 +1,233 @@ +package message + +import ( + "encoding/binary" + + "github.com/pkg/errors" +) + +var _ IKEPayload = &SecurityAssociation{} + +type SecurityAssociation struct { + Proposals ProposalContainer +} + +type ProposalContainer []*Proposal + +type Proposal struct { + ProposalNumber uint8 + ProtocolID uint8 + SPI []byte + EncryptionAlgorithm TransformContainer + PseudorandomFunction TransformContainer + IntegrityAlgorithm TransformContainer + DiffieHellmanGroup TransformContainer + ExtendedSequenceNumbers TransformContainer +} + +type TransformContainer []*Transform + +type Transform struct { + TransformType uint8 + TransformID uint16 + AttributePresent bool + AttributeFormat uint8 + AttributeType uint16 + AttributeValue uint16 + VariableLengthAttributeValue []byte +} + +func (securityAssociation *SecurityAssociation) Type() IKEPayloadType { return TypeSA } + +func (securityAssociation *SecurityAssociation) marshal() ([]byte, error) { + securityAssociationData := make([]byte, 0) + + for proposalIndex, proposal := range securityAssociation.Proposals { + proposalData := make([]byte, 8) + + if (proposalIndex + 1) < len(securityAssociation.Proposals) { + proposalData[0] = 2 + } else { + proposalData[0] = 0 + } + + proposalData[4] = proposal.ProposalNumber + proposalData[5] = proposal.ProtocolID + + numberofSPI := len(proposal.SPI) + if numberofSPI > 0xFF { + return nil, errors.Errorf("Proposal: Too many SPI: %d", numberofSPI) + } + proposalData[6] = uint8(numberofSPI) + if len(proposal.SPI) > 0 { + proposalData = append(proposalData, proposal.SPI...) + } + + // combine all transforms + var transformList []*Transform + transformList = append(transformList, proposal.EncryptionAlgorithm...) + transformList = append(transformList, proposal.PseudorandomFunction...) + transformList = append(transformList, proposal.IntegrityAlgorithm...) + transformList = append(transformList, proposal.DiffieHellmanGroup...) + transformList = append(transformList, proposal.ExtendedSequenceNumbers...) + + if len(transformList) == 0 { + return nil, errors.Errorf("One proposal has no any transform") + } + + transformListCount := len(transformList) + if transformListCount > 0xFF { + return nil, errors.Errorf("Transform: Too many transform: %d", transformListCount) + } + proposalData[7] = uint8(transformListCount) + + proposalTransformData := make([]byte, 0) + + for transformIndex, transform := range transformList { + transformData := make([]byte, 8) + + if (transformIndex + 1) < len(transformList) { + transformData[0] = 3 + } else { + transformData[0] = 0 + } + + transformData[4] = transform.TransformType + binary.BigEndian.PutUint16(transformData[6:8], transform.TransformID) + + if transform.AttributePresent { + attributeData := make([]byte, 4) + + if transform.AttributeFormat == 0 { + // TLV + if len(transform.VariableLengthAttributeValue) == 0 { + return nil, errors.Errorf("Attribute of one transform not specified") + } + attributeFormatAndType := ((uint16(transform.AttributeFormat) & 0x1) << 15) | transform.AttributeType + binary.BigEndian.PutUint16(attributeData[0:2], attributeFormatAndType) + variableLen := len(transform.VariableLengthAttributeValue) + if variableLen > 0xFFFF { + return nil, errors.Errorf("VariableLengthAttributeValue length exceeds uint16 limit: %d", variableLen) + } + binary.BigEndian.PutUint16(attributeData[2:4], uint16(variableLen)) + attributeData = append(attributeData, transform.VariableLengthAttributeValue...) + } else { + // TV + attributeFormatAndType := ((uint16(transform.AttributeFormat) & 0x1) << 15) | transform.AttributeType + binary.BigEndian.PutUint16(attributeData[0:2], attributeFormatAndType) + binary.BigEndian.PutUint16(attributeData[2:4], transform.AttributeValue) + } + + transformData = append(transformData, attributeData...) + } + transformDataLen := len(transformData) + if transformDataLen > 0xFFFF { + return nil, errors.Errorf("Transform: transformData length exceeds uint16 limit: %d", transformDataLen) + } + binary.BigEndian.PutUint16(transformData[2:4], uint16(transformDataLen)) + + proposalTransformData = append(proposalTransformData, transformData...) + } + + proposalData = append(proposalData, proposalTransformData...) + proposalDataLen := len(proposalData) + if proposalDataLen > 0xFFFF { + return nil, errors.Errorf("Proposal: proposalData length exceeds uint16 limit: %d", proposalDataLen) + } + binary.BigEndian.PutUint16(proposalData[2:4], uint16(proposalDataLen)) + + securityAssociationData = append(securityAssociationData, proposalData...) + } + + return securityAssociationData, nil +} + +func (securityAssociation *SecurityAssociation) unmarshal(b []byte) error { + for len(b) > 0 { + // bounds checking + if len(b) < 8 { + return errors.Errorf("Proposal: No sufficient bytes to decode next proposal") + } + proposalLength := binary.BigEndian.Uint16(b[2:4]) + if proposalLength < 8 { + return errors.Errorf("Proposal: Illegal payload length %d < header length 8", proposalLength) + } + if len(b) < int(proposalLength) { + return errors.Errorf("Proposal: The length of received message not matchs the length specified in header") + } + + proposal := new(Proposal) + var transformData []byte + + proposal.ProposalNumber = b[4] + proposal.ProtocolID = b[5] + + spiSize := b[6] + if spiSize > 0 { + // bounds checking + if len(b) < int(8+spiSize) { + return errors.Errorf("Proposal: No sufficient bytes for unmarshalling SPI of proposal") + } + proposal.SPI = append(proposal.SPI, b[8:8+spiSize]...) + } + + transformData = b[8+spiSize : proposalLength] + + for len(transformData) > 0 { + // bounds checking + if len(transformData) < 8 { + return errors.Errorf("Transform: No sufficient bytes to decode next transform") + } + transformLength := binary.BigEndian.Uint16(transformData[2:4]) + if transformLength < 8 { + return errors.Errorf("Transform: Illegal payload length %d < header length 8", transformLength) + } + if len(transformData) < int(transformLength) { + return errors.Errorf("Transform: The length of received message not matchs the length specified in header") + } + + transform := new(Transform) + + transform.TransformType = transformData[4] + transform.TransformID = binary.BigEndian.Uint16(transformData[6:8]) + if transformLength > 8 { + transform.AttributePresent = true + transform.AttributeFormat = ((transformData[8] & 0x80) >> 7) + transform.AttributeType = binary.BigEndian.Uint16(transformData[8:10]) & 0x7f + + if transform.AttributeFormat == 0 { + attributeLength := binary.BigEndian.Uint16(transformData[10:12]) + // bounds checking + if (12 + attributeLength) != transformLength { + return errors.Errorf("Illegal attribute length %d not satisfies the transform length %d", + attributeLength, transformLength) + } + copy(transform.VariableLengthAttributeValue, transformData[12:12+attributeLength]) + } else { + transform.AttributeValue = binary.BigEndian.Uint16(transformData[10:12]) + } + } + + switch transform.TransformType { + case TypeEncryptionAlgorithm: + proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, transform) + case TypePseudorandomFunction: + proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, transform) + case TypeIntegrityAlgorithm: + proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, transform) + case TypeDiffieHellmanGroup: + proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, transform) + case TypeExtendedSequenceNumbers: + proposal.ExtendedSequenceNumbers = append(proposal.ExtendedSequenceNumbers, transform) + } + + transformData = transformData[transformLength:] + } + + securityAssociation.Proposals = append(securityAssociation.Proposals, proposal) + + b = b[proposalLength:] + } + + return nil +} diff --git a/message/payload_securityassociation_test.go b/message/payload_securityassociation_test.go new file mode 100644 index 0000000..5db1a10 --- /dev/null +++ b/message/payload_securityassociation_test.go @@ -0,0 +1,291 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validSecurityAssociation = &SecurityAssociation{ + ProposalContainer{ + &Proposal{ + ProposalNumber: 1, + ProtocolID: 1, + SPI: []byte{1, 2, 3}, + EncryptionAlgorithm: TransformContainer{ + &Transform{ + TransformType: TypeEncryptionAlgorithm, + TransformID: ENCR_AES_CBC, + AttributePresent: true, + AttributeFormat: AttributeFormatUseTV, + AttributeType: AttributeTypeKeyLength, + AttributeValue: 256, + }, + &Transform{ + TransformType: TypeEncryptionAlgorithm, + TransformID: ENCR_AES_CBC, + AttributePresent: true, + AttributeFormat: AttributeFormatUseTV, + AttributeType: AttributeTypeKeyLength, + AttributeValue: 192, + }, + }, + IntegrityAlgorithm: TransformContainer{ + &Transform{ + TransformType: TypeIntegrityAlgorithm, + TransformID: AUTH_HMAC_MD5_96, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + &Transform{ + TransformType: TypeIntegrityAlgorithm, + TransformID: AUTH_HMAC_SHA1_96, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + DiffieHellmanGroup: TransformContainer{ + &Transform{ + TransformType: TypeDiffieHellmanGroup, + TransformID: DH_1024_BIT_MODP, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + &Transform{ + TransformType: TypeDiffieHellmanGroup, + TransformID: DH_2048_BIT_MODP, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + PseudorandomFunction: TransformContainer{ + &Transform{ + TransformType: TypePseudorandomFunction, + TransformID: PRF_HMAC_MD5, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + &Transform{ + TransformType: TypePseudorandomFunction, + TransformID: PRF_HMAC_SHA1, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + ExtendedSequenceNumbers: TransformContainer{ + &Transform{ + TransformType: TypeExtendedSequenceNumbers, + TransformID: ESN_DISABLE, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + }, + &Proposal{ + ProposalNumber: 2, + ProtocolID: 1, + SPI: []byte{1, 2, 3}, + EncryptionAlgorithm: TransformContainer{ + &Transform{ + TransformType: TypeEncryptionAlgorithm, + TransformID: ENCR_AES_CBC, + AttributePresent: true, + AttributeFormat: AttributeFormatUseTV, + AttributeType: AttributeTypeKeyLength, + AttributeValue: 128, + }, + }, + IntegrityAlgorithm: TransformContainer{ + &Transform{ + TransformType: TypeIntegrityAlgorithm, + TransformID: AUTH_HMAC_SHA2_256_128, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + DiffieHellmanGroup: TransformContainer{ + &Transform{ + TransformType: TypeDiffieHellmanGroup, + TransformID: DH_1024_BIT_MODP, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + PseudorandomFunction: TransformContainer{ + &Transform{ + TransformType: TypePseudorandomFunction, + TransformID: PRF_HMAC_SHA2_256, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + ExtendedSequenceNumbers: TransformContainer{ + &Transform{ + TransformType: TypeExtendedSequenceNumbers, + TransformID: ESN_DISABLE, + AttributePresent: false, + AttributeType: 0, + AttributeValue: 0, + }, + }, + }, + }, + } + + validSecurityAssociationByte = []byte{ + 0x02, 0x00, 0x00, 0x5b, 0x01, 0x01, 0x03, 0x09, + 0x01, 0x02, 0x03, 0x03, 0x00, 0x00, 0x0c, 0x01, + 0x00, 0x00, 0x0c, 0x80, 0x0e, 0x01, 0x00, 0x03, + 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x0c, 0x80, + 0x0e, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x08, 0x02, + 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x08, 0x02, + 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x08, 0x03, + 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x08, 0x03, + 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x08, 0x04, + 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x08, 0x04, + 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x08, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x02, + 0x01, 0x03, 0x05, 0x01, 0x02, 0x03, 0x03, 0x00, + 0x00, 0x0c, 0x01, 0x00, 0x00, 0x0c, 0x80, 0x0e, + 0x00, 0x80, 0x03, 0x00, 0x00, 0x08, 0x02, 0x00, + 0x00, 0x05, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, + 0x00, 0x0c, 0x03, 0x00, 0x00, 0x08, 0x04, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x05, 0x00, + 0x00, 0x00, + } +) + +func TestSecurityAssociationMarshal(t *testing.T) { + testcases := []struct { + description string + securityAssociation *SecurityAssociation + expErr bool + expMarshal []byte + }{ + { + description: "One proposal doesn't have any transform", + securityAssociation: &SecurityAssociation{ + ProposalContainer{ + &Proposal{ + ProposalNumber: 1, + ProtocolID: 1, + SPI: []byte{1, 2, 3}, + }, + }, + }, + expErr: true, + }, + { + description: "Attribute of one transform not specified", + securityAssociation: &SecurityAssociation{ + ProposalContainer{ + &Proposal{ + ProposalNumber: 1, + ProtocolID: 1, + SPI: []byte{1, 2, 3}, + EncryptionAlgorithm: TransformContainer{ + &Transform{ + TransformType: TypeEncryptionAlgorithm, + TransformID: ENCR_AES_CBC, + AttributePresent: true, + AttributeFormat: 0, + AttributeType: AttributeTypeKeyLength, + }, + }, + }, + }, + }, + expErr: true, + }, + { + description: "SecurityAssociation Marshal", + securityAssociation: validSecurityAssociation, + expMarshal: validSecurityAssociationByte, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.securityAssociation.marshal() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + } + }) + } +} + +func TestSecurityAssociationUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expErr bool + expSA *SecurityAssociation + }{ + { + description: "No sufficient bytes to decode next proposal", + b: []byte{0x01, 0x02, 0x03, 0x04}, + expErr: true, + }, + { + description: "Illegal payload length", + b: []byte{0x01, 0x02, 0x00, 0x04, 0x05, 0x06, 0x07, 0x08}, + expErr: true, + }, + { + description: "The length of received message not matchs the length specified in header", + b: []byte{0x01, 0x02, 0x00, 0x09, 0x05, 0x06, 0x07, 0x08}, + expErr: true, + }, + { + description: "No sufficient bytes for unmarshalling SPI of proposal", + b: []byte{ + 0x01, 0x02, 0x00, 0x09, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, + }, + expErr: true, + }, + { + description: "Illegal attribute length", + b: []byte{ + 0x00, 0x00, 0x00, 0x18, 0x02, 0x01, 0x03, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x00, 0x00, 0x0d, 0x03, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x05, 0x01, + }, + expErr: true, + }, + { + description: "SecurityAssociation Unmarshal", + b: validSecurityAssociationByte, + expSA: validSecurityAssociation, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var sa SecurityAssociation + err := sa.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, *tc.expSA, sa) + } + }) + } +} diff --git a/message/payload_trafficselectorinitiator.go b/message/payload_trafficselectorinitiator.go new file mode 100644 index 0000000..b59e6a1 --- /dev/null +++ b/message/payload_trafficselectorinitiator.go @@ -0,0 +1,175 @@ +package message + +import ( + "encoding/binary" + + "github.com/pkg/errors" +) + +var _ IKEPayload = &TrafficSelectorInitiator{} + +type TrafficSelectorInitiator struct { + TrafficSelectors IndividualTrafficSelectorContainer +} + +type IndividualTrafficSelectorContainer []*IndividualTrafficSelector + +type IndividualTrafficSelector struct { + TSType uint8 + IPProtocolID uint8 + StartPort uint16 + EndPort uint16 + StartAddress []byte + EndAddress []byte +} + +func (trafficSelector *TrafficSelectorInitiator) Type() IKEPayloadType { return TypeTSi } + +func (trafficSelector *TrafficSelectorInitiator) marshal() ([]byte, error) { + if len(trafficSelector.TrafficSelectors) == 0 { + return nil, errors.Errorf("TrafficSelector: Contains no traffic selector for marshaling message") + } + + trafficSelectorData := make([]byte, 4) + + selectorCount := len(trafficSelector.TrafficSelectors) + + if selectorCount > 0xFF { + return nil, errors.Errorf("TrafficSelector: too many traffic selectors: %d", selectorCount) + } + + trafficSelectorData[0] = uint8(selectorCount) + + for _, individualTrafficSelector := range trafficSelector.TrafficSelectors { + if individualTrafficSelector.TSType == TS_IPV4_ADDR_RANGE { + // Address length checking + if len(individualTrafficSelector.StartAddress) != 4 { + return nil, errors.Errorf("TrafficSelector: Start IPv4 address length is not correct") + } + if len(individualTrafficSelector.EndAddress) != 4 { + return nil, errors.Errorf("TrafficSelector: End IPv4 address length is not correct") + } + + individualTrafficSelectorData := make([]byte, 8) + + individualTrafficSelectorData[0] = individualTrafficSelector.TSType + individualTrafficSelectorData[1] = individualTrafficSelector.IPProtocolID + binary.BigEndian.PutUint16(individualTrafficSelectorData[4:6], individualTrafficSelector.StartPort) + binary.BigEndian.PutUint16(individualTrafficSelectorData[6:8], individualTrafficSelector.EndPort) + + individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.StartAddress...) + individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.EndAddress...) + + dataLen := len(individualTrafficSelectorData) + if dataLen > 0xFFFF { + return nil, errors.Errorf("TrafficSelector: individualTrafficSelectorData length exceeds uint16 "+ + "maximum value: %v", dataLen) + } + binary.BigEndian.PutUint16(individualTrafficSelectorData[2:4], uint16(dataLen)) + + trafficSelectorData = append(trafficSelectorData, individualTrafficSelectorData...) + } else if individualTrafficSelector.TSType == TS_IPV6_ADDR_RANGE { + // Address length checking + if len(individualTrafficSelector.StartAddress) != 16 { + return nil, errors.Errorf("TrafficSelector: Start IPv6 address length is not correct") + } + if len(individualTrafficSelector.EndAddress) != 16 { + return nil, errors.Errorf("TrafficSelector: End IPv6 address length is not correct") + } + + individualTrafficSelectorData := make([]byte, 8) + + individualTrafficSelectorData[0] = individualTrafficSelector.TSType + individualTrafficSelectorData[1] = individualTrafficSelector.IPProtocolID + binary.BigEndian.PutUint16(individualTrafficSelectorData[4:6], individualTrafficSelector.StartPort) + binary.BigEndian.PutUint16(individualTrafficSelectorData[6:8], individualTrafficSelector.EndPort) + + individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.StartAddress...) + individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.EndAddress...) + + dataLen := len(individualTrafficSelectorData) + if dataLen > 0xFFFF { + return nil, errors.Errorf("TrafficSelector: individualTrafficSelectorData length exceeds uint16 "+ + "maximum value: %v", dataLen) + } + binary.BigEndian.PutUint16(individualTrafficSelectorData[2:4], uint16(dataLen)) + + trafficSelectorData = append(trafficSelectorData, individualTrafficSelectorData...) + } else { + return nil, errors.Errorf("TrafficSelector: Unsupported traffic selector type") + } + } + + return trafficSelectorData, nil +} + +func (trafficSelector *TrafficSelectorInitiator) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) < 4 { + return errors.Errorf("TrafficSelector: No sufficient bytes to get number of traffic selector in header") + } + + numberOfSPI := b[0] + + b = b[4:] + + for ; numberOfSPI > 0; numberOfSPI-- { + // bounds checking + if len(b) < 4 { + return errors.Errorf( + "TrafficSelector: No sufficient bytes to decode next individual traffic selector length in header") + } + trafficSelectorType := b[0] + if trafficSelectorType == TS_IPV4_ADDR_RANGE { + selectorLength := binary.BigEndian.Uint16(b[2:4]) + if selectorLength != 16 { + return errors.Errorf("TrafficSelector: A TS_IPV4_ADDR_RANGE type traffic selector should has length 16 bytes") + } + if len(b) < int(selectorLength) { + return errors.Errorf("TrafficSelector: No sufficient bytes to decode next individual traffic selector") + } + + individualTrafficSelector := &IndividualTrafficSelector{} + + individualTrafficSelector.TSType = b[0] + individualTrafficSelector.IPProtocolID = b[1] + individualTrafficSelector.StartPort = binary.BigEndian.Uint16(b[4:6]) + individualTrafficSelector.EndPort = binary.BigEndian.Uint16(b[6:8]) + + individualTrafficSelector.StartAddress = append(individualTrafficSelector.StartAddress, b[8:12]...) + individualTrafficSelector.EndAddress = append(individualTrafficSelector.EndAddress, b[12:16]...) + + trafficSelector.TrafficSelectors = append(trafficSelector.TrafficSelectors, individualTrafficSelector) + + b = b[16:] + } else if trafficSelectorType == TS_IPV6_ADDR_RANGE { + selectorLength := binary.BigEndian.Uint16(b[2:4]) + if selectorLength != 40 { + return errors.Errorf("TrafficSelector: A TS_IPV6_ADDR_RANGE type traffic selector should has length 40 bytes") + } + if len(b) < int(selectorLength) { + return errors.Errorf("TrafficSelector: No sufficient bytes to decode next individual traffic selector") + } + + individualTrafficSelector := &IndividualTrafficSelector{} + + individualTrafficSelector.TSType = b[0] + individualTrafficSelector.IPProtocolID = b[1] + individualTrafficSelector.StartPort = binary.BigEndian.Uint16(b[4:6]) + individualTrafficSelector.EndPort = binary.BigEndian.Uint16(b[6:8]) + + individualTrafficSelector.StartAddress = append(individualTrafficSelector.StartAddress, b[8:24]...) + individualTrafficSelector.EndAddress = append(individualTrafficSelector.EndAddress, b[24:40]...) + + trafficSelector.TrafficSelectors = append(trafficSelector.TrafficSelectors, individualTrafficSelector) + + b = b[40:] + } else { + return errors.Errorf("TrafficSelector: Unsupported traffic selector type") + } + } + } + + return nil +} diff --git a/message/payload_trafficselectorinitiator_test.go b/message/payload_trafficselectorinitiator_test.go new file mode 100644 index 0000000..6b8286a --- /dev/null +++ b/message/payload_trafficselectorinitiator_test.go @@ -0,0 +1,222 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validTSIIPv4 = TrafficSelectorInitiator{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + TSType: TS_IPV4_ADDR_RANGE, + IPProtocolID: IPProtocolAll, + StartPort: 0, + EndPort: 65535, + StartAddress: []byte{0x0a, 0x00, 0x00, 0x01}, + EndAddress: []byte{0x0a, 0x00, 0x00, 0x01}, + }, + }, + } + validTSIIPv4Byte = []byte{ + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x10, + 0x00, 0x00, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x01, + 0x0a, 0x00, 0x00, 0x01, + } + + validTSIIPv6 = TrafficSelectorInitiator{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + TSType: TS_IPV6_ADDR_RANGE, + IPProtocolID: IPProtocolAll, + StartPort: 0, + EndPort: 65535, + StartAddress: []byte{ + 0xb8, 0x46, 0xd2, 0x47, 0xcf, 0x84, 0xf2, 0x89, + 0xcf, 0x7e, 0xce, 0xe6, 0x6d, 0xb2, 0x1f, 0xc4, + }, + EndAddress: []byte{ + 0xb8, 0x46, 0xd2, 0x47, 0xcf, 0x84, 0xf2, 0x89, + 0xcf, 0x7e, 0xce, 0xe6, 0x6d, 0xb2, 0x1f, 0xc4, + }, + }, + }, + } + + validTSIIPv6Byte = []byte{ + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x28, + 0x00, 0x00, 0xff, 0xff, 0xb8, 0x46, 0xd2, 0x47, + 0xcf, 0x84, 0xf2, 0x89, 0xcf, 0x7e, 0xce, 0xe6, + 0x6d, 0xb2, 0x1f, 0xc4, 0xb8, 0x46, 0xd2, 0x47, + 0xcf, 0x84, 0xf2, 0x89, 0xcf, 0x7e, 0xce, 0xe6, + 0x6d, 0xb2, 0x1f, 0xc4, + } +) + +func TestTrafficSelectorInitiatorMarshal(t *testing.T) { + testcases := []struct { + description string + tsi TrafficSelectorInitiator + expMarshal []byte + expErr bool + }{ + { + description: "Contains no traffic selector for marshaling message", + tsi: TrafficSelectorInitiator{}, + expErr: true, + }, + { + description: "Unsupported traffic selector type", + tsi: TrafficSelectorInitiator{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + StartAddress: []byte{0x01, 0x02, 0x03}, + }, + }, + }, + expErr: true, + }, + { + description: "Start IPv4 address length is not correct", + tsi: TrafficSelectorInitiator{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + TSType: TS_IPV4_ADDR_RANGE, + StartAddress: []byte{0x01, 0x02, 0x03}, + }, + }, + }, + expErr: true, + }, + { + description: "End IPv4 address length is not correct", + tsi: TrafficSelectorInitiator{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + TSType: TS_IPV4_ADDR_RANGE, + StartAddress: []byte{0x01, 0x02, 0x03, 0x04}, + EndAddress: []byte{0x01, 0x02, 0x03}, + }, + }, + }, + expErr: true, + }, + { + description: "TrafficSelectorInitiator Marshal IPv4", + tsi: validTSIIPv4, + expMarshal: validTSIIPv4Byte, + expErr: false, + }, + { + description: "TrafficSelectorInitiator Marshal IPv6", + tsi: validTSIIPv6, + expMarshal: validTSIIPv6Byte, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.tsi.marshal() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + } + }) + } +} + +func TestTrafficSelectorInitiatorUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expMarshal TrafficSelectorInitiator + expErr bool + }{ + { + description: "No sufficient bytes to get number of traffic selector in header", + b: []byte{0x01, 0x02, 0x03}, + expErr: true, + }, + { + description: "No sufficient bytes to decode next individual traffic selector length in header", + b: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, + expErr: true, + }, + { + description: "A TS_IPV4_ADDR_RANGE type traffic selector should has length 16 bytes", + b: []byte{ + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x05, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + }, + expErr: true, + }, + { + description: "TS_IPV4_ADDR_RANGE No sufficient bytes to decode next individual traffic selector", + b: []byte{ + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x10, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, + }, + expErr: true, + }, + { + description: "A TS_IPV6_ADDR_RANGE type traffic selector should has length 40 bytes", + b: []byte{ + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x27, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + }, + expErr: true, + }, + { + description: "TS_IPV6_ADDR_RANGE No sufficient bytes to decode next individual traffic selector", + b: []byte{ + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x28, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x10, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + }, + expErr: true, + }, + { + description: "Unsupported traffic selector type", + b: []byte{ + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x27, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + }, + expErr: true, + }, + { + description: "TrafficSelectorInitiator Unmarshal IPv4", + b: validTSIIPv4Byte, + expMarshal: validTSIIPv4, + expErr: false, + }, + { + description: "TrafficSelectorInitiator Unmarshal IPv6", + b: validTSIIPv6Byte, + expMarshal: validTSIIPv6, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var tsi TrafficSelectorInitiator + err := tsi.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, tsi) + } + }) + } +} diff --git a/message/payload_trafficselectorresponder.go b/message/payload_trafficselectorresponder.go new file mode 100644 index 0000000..fd25c9c --- /dev/null +++ b/message/payload_trafficselectorresponder.go @@ -0,0 +1,162 @@ +package message + +import ( + "encoding/binary" + + "github.com/pkg/errors" +) + +var _ IKEPayload = &TrafficSelectorResponder{} + +type TrafficSelectorResponder struct { + TrafficSelectors IndividualTrafficSelectorContainer +} + +func (trafficSelector *TrafficSelectorResponder) Type() IKEPayloadType { return TypeTSr } + +func (trafficSelector *TrafficSelectorResponder) marshal() ([]byte, error) { + if len(trafficSelector.TrafficSelectors) > 0 { + trafficSelectorData := make([]byte, 4) + selectorCount := len(trafficSelector.TrafficSelectors) + + if selectorCount > 0xFF { + return nil, errors.Errorf("TrafficSelector: too many traffic selectors: %d", selectorCount) + } + + trafficSelectorData[0] = uint8(selectorCount) + + for _, individualTrafficSelector := range trafficSelector.TrafficSelectors { + if individualTrafficSelector.TSType == TS_IPV4_ADDR_RANGE { + // Address length checking + if len(individualTrafficSelector.StartAddress) != 4 { + return nil, errors.Errorf("TrafficSelector: Start IPv4 address length is not correct") + } + if len(individualTrafficSelector.EndAddress) != 4 { + return nil, errors.Errorf("TrafficSelector: End IPv4 address length is not correct") + } + + individualTrafficSelectorData := make([]byte, 8) + + individualTrafficSelectorData[0] = individualTrafficSelector.TSType + individualTrafficSelectorData[1] = individualTrafficSelector.IPProtocolID + binary.BigEndian.PutUint16(individualTrafficSelectorData[4:6], individualTrafficSelector.StartPort) + binary.BigEndian.PutUint16(individualTrafficSelectorData[6:8], individualTrafficSelector.EndPort) + + individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.StartAddress...) + individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.EndAddress...) + + dataLen := len(individualTrafficSelectorData) + if dataLen > 0xFFFF { + return nil, errors.Errorf("TrafficSelector: individualTrafficSelectorData length exceeds uint16 "+ + "maximum value: %v", dataLen) + } + binary.BigEndian.PutUint16(individualTrafficSelectorData[2:4], uint16(dataLen)) + + trafficSelectorData = append(trafficSelectorData, individualTrafficSelectorData...) + } else if individualTrafficSelector.TSType == TS_IPV6_ADDR_RANGE { + // Address length checking + if len(individualTrafficSelector.StartAddress) != 16 { + return nil, errors.Errorf("TrafficSelector: Start IPv6 address length is not correct") + } + if len(individualTrafficSelector.EndAddress) != 16 { + return nil, errors.Errorf("TrafficSelector: End IPv6 address length is not correct") + } + + individualTrafficSelectorData := make([]byte, 8) + + individualTrafficSelectorData[0] = individualTrafficSelector.TSType + individualTrafficSelectorData[1] = individualTrafficSelector.IPProtocolID + binary.BigEndian.PutUint16(individualTrafficSelectorData[4:6], individualTrafficSelector.StartPort) + binary.BigEndian.PutUint16(individualTrafficSelectorData[6:8], individualTrafficSelector.EndPort) + + individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.StartAddress...) + individualTrafficSelectorData = append(individualTrafficSelectorData, individualTrafficSelector.EndAddress...) + + dataLen := len(individualTrafficSelectorData) + if dataLen > 0xFFFF { + return nil, errors.Errorf("TrafficSelector: individualTrafficSelectorData length exceeds uint16 "+ + "maximum value: %v", dataLen) + } + binary.BigEndian.PutUint16(individualTrafficSelectorData[2:4], uint16(dataLen)) + + trafficSelectorData = append(trafficSelectorData, individualTrafficSelectorData...) + } else { + return nil, errors.Errorf("TrafficSelector: Unsupported traffic selector type") + } + } + + return trafficSelectorData, nil + } else { + return nil, errors.Errorf("TrafficSelector: Contains no traffic selector for marshaling message") + } +} + +func (trafficSelector *TrafficSelectorResponder) unmarshal(b []byte) error { + if len(b) > 0 { + // bounds checking + if len(b) < 4 { + return errors.Errorf("TrafficSelector: No sufficient bytes to get number of traffic selector in header") + } + + numberOfSPI := b[0] + + b = b[4:] + for ; numberOfSPI > 0; numberOfSPI-- { + // bounds checking + if len(b) < 4 { + return errors.Errorf( + "TrafficSelector: No sufficient bytes to decode next individual traffic selector length in header") + } + trafficSelectorType := b[0] + if trafficSelectorType == TS_IPV4_ADDR_RANGE { + selectorLength := binary.BigEndian.Uint16(b[2:4]) + if selectorLength != 16 { + return errors.Errorf("TrafficSelector: A TS_IPV4_ADDR_RANGE type traffic selector should has length 16 bytes") + } + if len(b) < int(selectorLength) { + return errors.Errorf("TrafficSelector: No sufficient bytes to decode next individual traffic selector") + } + + individualTrafficSelector := &IndividualTrafficSelector{} + + individualTrafficSelector.TSType = b[0] + individualTrafficSelector.IPProtocolID = b[1] + individualTrafficSelector.StartPort = binary.BigEndian.Uint16(b[4:6]) + individualTrafficSelector.EndPort = binary.BigEndian.Uint16(b[6:8]) + + individualTrafficSelector.StartAddress = append(individualTrafficSelector.StartAddress, b[8:12]...) + individualTrafficSelector.EndAddress = append(individualTrafficSelector.EndAddress, b[12:16]...) + + trafficSelector.TrafficSelectors = append(trafficSelector.TrafficSelectors, individualTrafficSelector) + + b = b[16:] + } else if trafficSelectorType == TS_IPV6_ADDR_RANGE { + selectorLength := binary.BigEndian.Uint16(b[2:4]) + if selectorLength != 40 { + return errors.Errorf("TrafficSelector: A TS_IPV6_ADDR_RANGE type traffic selector should has length 40 bytes") + } + if len(b) < int(selectorLength) { + return errors.Errorf("TrafficSelector: No sufficient bytes to decode next individual traffic selector") + } + + individualTrafficSelector := &IndividualTrafficSelector{} + + individualTrafficSelector.TSType = b[0] + individualTrafficSelector.IPProtocolID = b[1] + individualTrafficSelector.StartPort = binary.BigEndian.Uint16(b[4:6]) + individualTrafficSelector.EndPort = binary.BigEndian.Uint16(b[6:8]) + + individualTrafficSelector.StartAddress = append(individualTrafficSelector.StartAddress, b[8:24]...) + individualTrafficSelector.EndAddress = append(individualTrafficSelector.EndAddress, b[24:40]...) + + trafficSelector.TrafficSelectors = append(trafficSelector.TrafficSelectors, individualTrafficSelector) + + b = b[40:] + } else { + return errors.Errorf("TrafficSelector: Unsupported traffic selector type") + } + } + } + + return nil +} diff --git a/message/payload_trafficselectorresponder_test.go b/message/payload_trafficselectorresponder_test.go new file mode 100644 index 0000000..7ce08ac --- /dev/null +++ b/message/payload_trafficselectorresponder_test.go @@ -0,0 +1,222 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validTSRIPv4 = TrafficSelectorResponder{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + TSType: TS_IPV4_ADDR_RANGE, + IPProtocolID: IPProtocolAll, + StartPort: 0, + EndPort: 65535, + StartAddress: []byte{0x0a, 0x00, 0x00, 0x01}, + EndAddress: []byte{0x0a, 0x00, 0x00, 0x01}, + }, + }, + } + validTSRIPv4Byte = []byte{ + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x10, + 0x00, 0x00, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x01, + 0x0a, 0x00, 0x00, 0x01, + } + + validTSRIPv6 = TrafficSelectorResponder{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + TSType: TS_IPV6_ADDR_RANGE, + IPProtocolID: IPProtocolAll, + StartPort: 0, + EndPort: 65535, + StartAddress: []byte{ + 0xb8, 0x46, 0xd2, 0x47, 0xcf, 0x84, 0xf2, 0x89, + 0xcf, 0x7e, 0xce, 0xe6, 0x6d, 0xb2, 0x1f, 0xc4, + }, + EndAddress: []byte{ + 0xb8, 0x46, 0xd2, 0x47, 0xcf, 0x84, 0xf2, 0x89, + 0xcf, 0x7e, 0xce, 0xe6, 0x6d, 0xb2, 0x1f, 0xc4, + }, + }, + }, + } + + validTSRIPv6Byte = []byte{ + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x28, + 0x00, 0x00, 0xff, 0xff, 0xb8, 0x46, 0xd2, 0x47, + 0xcf, 0x84, 0xf2, 0x89, 0xcf, 0x7e, 0xce, 0xe6, + 0x6d, 0xb2, 0x1f, 0xc4, 0xb8, 0x46, 0xd2, 0x47, + 0xcf, 0x84, 0xf2, 0x89, 0xcf, 0x7e, 0xce, 0xe6, + 0x6d, 0xb2, 0x1f, 0xc4, + } +) + +func TestTrafficSelectorResponderMarshal(t *testing.T) { + testcases := []struct { + description string + tsi TrafficSelectorResponder + expMarshal []byte + expErr bool + }{ + { + description: "Contains no traffic selector for marshaling message", + tsi: TrafficSelectorResponder{}, + expErr: true, + }, + { + description: "Unsupported traffic selector type", + tsi: TrafficSelectorResponder{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + StartAddress: []byte{0x01, 0x02, 0x03}, + }, + }, + }, + expErr: true, + }, + { + description: "Start IPv4 address length is not correct", + tsi: TrafficSelectorResponder{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + TSType: TS_IPV4_ADDR_RANGE, + StartAddress: []byte{0x01, 0x02, 0x03}, + }, + }, + }, + expErr: true, + }, + { + description: "End IPv4 address length is not correct", + tsi: TrafficSelectorResponder{ + IndividualTrafficSelectorContainer{ + &IndividualTrafficSelector{ + TSType: TS_IPV4_ADDR_RANGE, + StartAddress: []byte{0x01, 0x02, 0x03, 0x04}, + EndAddress: []byte{0x01, 0x02, 0x03}, + }, + }, + }, + expErr: true, + }, + { + description: "TrafficSelectorResponder Marshal IPv4", + tsi: validTSRIPv4, + expMarshal: validTSRIPv4Byte, + expErr: false, + }, + { + description: "TrafficSelectorResponder Marshal IPv6", + tsi: validTSRIPv6, + expMarshal: validTSRIPv6Byte, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.tsi.marshal() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + } + }) + } +} + +func TestTrafficSelectorResponderUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expMarshal TrafficSelectorResponder + expErr bool + }{ + { + description: "No sufficient bytes to get number of traffic selector in header", + b: []byte{0x01, 0x02, 0x03}, + expErr: true, + }, + { + description: "No sufficient bytes to decode next individual traffic selector length in header", + b: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, + expErr: true, + }, + { + description: "A TS_IPV4_ADDR_RANGE type traffic selector should has length 16 bytes", + b: []byte{ + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x05, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + }, + expErr: true, + }, + { + description: "TS_IPV4_ADDR_RANGE No sufficient bytes to decode next individual traffic selector", + b: []byte{ + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x10, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, + }, + expErr: true, + }, + { + description: "A TS_IPV6_ADDR_RANGE type traffic selector should has length 40 bytes", + b: []byte{ + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x27, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + }, + expErr: true, + }, + { + description: "TS_IPV6_ADDR_RANGE No sufficient bytes to decode next individual traffic selector", + b: []byte{ + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x28, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x10, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + }, + expErr: true, + }, + { + description: "Unsupported traffic selector type", + b: []byte{ + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x27, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + }, + expErr: true, + }, + { + description: "TrafficSelectorResponder Unmarshal IPv4", + b: validTSRIPv4Byte, + expMarshal: validTSRIPv4, + expErr: false, + }, + { + description: "TrafficSelectorResponder Unmarshal IPv6", + b: validTSRIPv6Byte, + expMarshal: validTSRIPv6, + expErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var tsi TrafficSelectorResponder + err := tsi.unmarshal(tc.b) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expMarshal, tsi) + } + }) + } +} diff --git a/message/payload_vendorid.go b/message/payload_vendorid.go new file mode 100644 index 0000000..ab00ca1 --- /dev/null +++ b/message/payload_vendorid.go @@ -0,0 +1,20 @@ +package message + +var _ IKEPayload = &VendorID{} + +type VendorID struct { + VendorIDData []byte +} + +func (vendorID *VendorID) Type() IKEPayloadType { return TypeV } + +func (vendorID *VendorID) marshal() ([]byte, error) { + return vendorID.VendorIDData, nil +} + +func (vendorID *VendorID) unmarshal(b []byte) error { + if len(b) > 0 { + vendorID.VendorIDData = append(vendorID.VendorIDData, b...) + } + return nil +} diff --git a/message/payload_vendorid_test.go b/message/payload_vendorid_test.go new file mode 100644 index 0000000..bae7016 --- /dev/null +++ b/message/payload_vendorid_test.go @@ -0,0 +1,68 @@ +package message + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + validVendorID = VendorID{ + VendorIDData: []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + }, + } + + validVendorIDByte = []byte{ + 0x7d, 0x09, 0x18, 0x42, 0x60, 0x9c, 0x9e, 0x20, + 0x56, 0x9f, 0xc0, 0x39, 0xda, 0x3f, 0x22, 0x2a, + 0xb8, 0x56, 0x81, 0x8a, + } +) + +func TestVendorIDMarshal(t *testing.T) { + testcases := []struct { + description string + vendorID VendorID + expMarshal []byte + }{ + { + description: "VendorID marshal", + vendorID: validVendorID, + expMarshal: validVendorIDByte, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + result, err := tc.vendorID.marshal() + require.NoError(t, err) + require.Equal(t, tc.expMarshal, result) + }) + } +} + +func TestVendorIDUnmarshal(t *testing.T) { + testcases := []struct { + description string + b []byte + expMarshal VendorID + }{ + { + description: "VendorID Unmarshal", + b: validVendorIDByte, + expMarshal: validVendorID, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + var vendorID VendorID + err := vendorID.unmarshal(tc.b) + require.NoError(t, err) + require.Equal(t, tc.expMarshal, vendorID) + }) + } +} diff --git a/types/types.go b/message/types.go similarity index 85% rename from types/types.go rename to message/types.go index e11e3a6..12901c0 100644 --- a/types/types.go +++ b/message/types.go @@ -1,11 +1,11 @@ -package types +package message // IKE types type IKEPayloadType uint8 const ( - NoNext = 0 - TypeSA = iota + 32 + NoNext IKEPayloadType = 0 + TypeSA IKEPayloadType = iota + 32 TypeKE TypeIDi TypeIDr @@ -27,10 +27,10 @@ const ( type EAPType uint8 const ( - EAPTypeIdentity = iota + 1 + EAPTypeIdentity EAPType = iota + 1 EAPTypeNotification EAPTypeNak - EAPTypeExpanded = 254 + EAPTypeExpanded EAPType = 254 ) const ( @@ -80,6 +80,7 @@ const ( PRF_HMAC_MD5 = iota + 1 PRF_HMAC_SHA1 PRF_HMAC_TIGER + PRF_HMAC_SHA2_256 = 5 ) const ( @@ -89,6 +90,7 @@ const ( AUTH_DES_MAC AUTH_KPDK_MD5 AUTH_AES_XCBC_96 + AUTH_HMAC_SHA2_256_128 = 12 ) const ( @@ -139,6 +141,8 @@ const ( FAILED_CP_REQUIRED = 37 TS_UNACCEPTABLE = 38 INVALID_SELECTORS = 39 + UNACCEPTABLE_ADDRESSES = 40 + UNEXPECTED_NAT_DETECTED = 41 TEMPORARY_FAILURE = 43 CHILD_SA_NOT_FOUND = 44 INITIAL_CONTACT = 16384 @@ -153,6 +157,13 @@ const ( REKEY_SA = 16393 ESP_TFC_PADDING_NOT_SUPPORTED = 16394 NON_FIRST_FRAGMENTS_ALSO = 16395 + MOBIKE_SUPPORTED = 16396 + ADDITIONAL_IP4_ADDRESS = 16397 + ADDITIONAL_IP6_ADDRESS = 16398 + NO_ADDITIONAL_ADDRESSES = 16399 + UPDATE_SA_ADDRESSES = 16400 + COOKIE2 = 16401 + NO_NATS_ALLOWED = 16402 ) // Protocol ID @@ -258,11 +269,18 @@ const ( ANParametersTypeEstablishmentCause = 4 ) +// Used for checking if AN-Parameter length field is legal +const ( + ANParametersLenGUAMI = 6 + ANParametersLenPLMNID = 3 + ANParametersLenEstCause = 1 +) + // Used in IE Establishment Cause field for cause types const ( EstablishmentCauseEmergency = 0 EstablishmentCauseHighPriorityAccess = 1 - EstablishmentCauseMO_Signalling = 3 + EstablishmentCauseMO_Signaling = 3 EstablishmentCauseMO_Data = 4 EstablishmentCauseMPS_PriorityAccess = 8 EstablishmentCauseMCS_PriorityAccess = 9 @@ -271,7 +289,6 @@ const ( // Spare const EAP5GSpareValue = 0 -// 3GPP specified IKE Notify // 3GPP specified IKE Notify Message Types const ( Vendor3GPPNotifyType5G_QOS_INFO uint16 = 55501 @@ -282,12 +299,14 @@ const ( // Used in NotifyType5G_QOS_INFO const ( - NotifyType5G_QOS_INFOBitDSCPICheck uint8 = 1 - NotifyType5G_QOS_INFOBitDCSICheck uint8 = 1 << 1 + NotifyType5G_QOS_INFOBitDSCPICheck uint8 = 1 << iota + NotifyType5G_QOS_INFOBitDCSICheck ) // IKE role +type Role bool + const ( - Role_Initiator = iota - Role_Responder + Role_Initiator Role = true + Role_Responder Role = false ) diff --git a/internal/types/types.go b/security/IKECrypto/ikeCrypto.go similarity index 89% rename from internal/types/types.go rename to security/IKECrypto/ikeCrypto.go index c885f6a..6d50330 100644 --- a/internal/types/types.go +++ b/security/IKECrypto/ikeCrypto.go @@ -1,4 +1,4 @@ -package types +package crypto // Interfaces type IKECrypto interface { diff --git a/internal/dh/dh.go b/security/dh/dh.go similarity index 60% rename from internal/dh/dh.go rename to security/dh/dh.go index 8d284ef..a32dfc0 100644 --- a/internal/dh/dh.go +++ b/security/dh/dh.go @@ -1,30 +1,21 @@ package dh import ( - "fmt" "math/big" - "github.com/sirupsen/logrus" - - "github.com/free5gc/ike/internal/logger" "github.com/free5gc/ike/message" - "github.com/free5gc/ike/types" ) var ( - dhLog *logrus.Entry dhString map[uint16]func(uint16, uint16, []byte) string dhTypes map[string]DHType ) func init() { - // Log - dhLog = logger.DHLog - // DH String dhString = make(map[uint16]func(uint16, uint16, []byte) string) - dhString[types.DH_1024_BIT_MODP] = toString_DH_1024_BIT_MODP - dhString[types.DH_2048_BIT_MODP] = toString_DH_2048_BIT_MODP + dhString[message.DH_1024_BIT_MODP] = toString_DH_1024_BIT_MODP + dhString[message.DH_2048_BIT_MODP] = toString_DH_2048_BIT_MODP // DH Types dhTypes = make(map[string]DHType) @@ -34,7 +25,6 @@ func init() { // Group 2: DH_1024_BIT_MODP factor, ok := new(big.Int).SetString(Group2PrimeString, 16) if !ok { - dhLog.Error("Error occurs when setting big number") panic("IKE Diffie Hellman Group failed to init.") } generator = new(big.Int).SetUint64(Group2Generator) @@ -47,7 +37,6 @@ func init() { // Group 14: DH_2048_BIT_MODP factor, ok = new(big.Int).SetString(Group14PrimeString, 16) if !ok { - dhLog.Error("Error occurs when setting big number") panic("IKE Diffie Hellman Group failed to init.") } generator = new(big.Int).SetUint64(Group14Generator) @@ -56,36 +45,6 @@ func init() { generator: generator, factorBytesLength: len(factor.Bytes()), } - - // Default Priority - priority := []string{ - string_DH_1024_BIT_MODP, - string_DH_2048_BIT_MODP, - } - - // Set Priority - for i, s := range priority { - if dhType, ok := dhTypes[s]; ok { - dhType.setPriority(uint32(i)) - } else { - dhLog.Error("No such DH group implementation") - panic("IKE Diffie Hellman Group failed to init.") - } - } -} - -func SetPriority(algolist map[string]uint32) error { - // check implemented - for algo := range algolist { - if _, ok := dhTypes[algo]; !ok { - return fmt.Errorf("No such implementation: %s", algo) - } - } - // set priority - for algo, priority := range algolist { - dhTypes[algo].setPriority(priority) - } - return nil } func StrToType(algo string) DHType { @@ -100,7 +59,7 @@ func DecodeTransform(transform *message.Transform) DHType { if f, ok := dhString[transform.TransformID]; ok { s := f(transform.AttributeType, transform.AttributeValue, transform.VariableLengthAttributeValue) if s != "" { - if dhType, ok := dhTypes[s]; ok { + if dhType, ok2 := dhTypes[s]; ok2 { return dhType } else { return nil @@ -115,20 +74,18 @@ func DecodeTransform(transform *message.Transform) DHType { func ToTransform(dhType DHType) *message.Transform { t := new(message.Transform) - t.TransformType = types.TypeDiffieHellmanGroup - t.TransformID = dhType.transformID() + t.TransformType = message.TypeDiffieHellmanGroup + t.TransformID = dhType.TransformID() t.AttributePresent, t.AttributeType, t.AttributeValue, t.VariableLengthAttributeValue = dhType.getAttribute() if t.AttributePresent && t.VariableLengthAttributeValue == nil { - t.AttributeFormat = types.AttributeFormatUseTV + t.AttributeFormat = message.AttributeFormatUseTV } return t } type DHType interface { - transformID() uint16 + TransformID() uint16 getAttribute() (bool, uint16, uint16, []byte) - setPriority(uint32) - Priority() uint32 GetSharedKey(secret, peerPublicValue *big.Int) []byte GetPublicValue(secret *big.Int) []byte } diff --git a/internal/dh/dh_1024_bit_modp.go b/security/dh/dh_1024_bit_modp.go similarity index 83% rename from internal/dh/dh_1024_bit_modp.go rename to security/dh/dh_1024_bit_modp.go index ca46989..69bf0db 100644 --- a/internal/dh/dh_1024_bit_modp.go +++ b/security/dh/dh_1024_bit_modp.go @@ -3,7 +3,7 @@ package dh import ( "math/big" - "github.com/free5gc/ike/types" + "github.com/free5gc/ike/message" ) const ( @@ -27,28 +27,19 @@ func toString_DH_1024_BIT_MODP(attrType uint16, intValue uint16, bytesValue []by var _ DHType = &DH_1024_BIT_MODP{} type DH_1024_BIT_MODP struct { - priority uint32 factor *big.Int generator *big.Int factorBytesLength int } -func (t *DH_1024_BIT_MODP) transformID() uint16 { - return types.DH_1024_BIT_MODP +func (t *DH_1024_BIT_MODP) TransformID() uint16 { + return message.DH_1024_BIT_MODP } func (t *DH_1024_BIT_MODP) getAttribute() (bool, uint16, uint16, []byte) { return false, 0, 0, nil } -func (t *DH_1024_BIT_MODP) setPriority(priority uint32) { - t.priority = priority -} - -func (t *DH_1024_BIT_MODP) Priority() uint32 { - return t.priority -} - func (t *DH_1024_BIT_MODP) GetSharedKey(secret, peerPublicValue *big.Int) []byte { sharedKey := new(big.Int).Exp(peerPublicValue, secret, t.factor).Bytes() prependZero := make([]byte, t.factorBytesLength-len(sharedKey)) diff --git a/internal/dh/dh_2048_bit_modp.go b/security/dh/dh_2048_bit_modp.go similarity index 85% rename from internal/dh/dh_2048_bit_modp.go rename to security/dh/dh_2048_bit_modp.go index fe6d74a..ba39c42 100644 --- a/internal/dh/dh_2048_bit_modp.go +++ b/security/dh/dh_2048_bit_modp.go @@ -3,7 +3,7 @@ package dh import ( "math/big" - "github.com/free5gc/ike/types" + "github.com/free5gc/ike/message" ) const ( @@ -35,28 +35,19 @@ func toString_DH_2048_BIT_MODP(attrType uint16, intValue uint16, bytesValue []by var _ DHType = &DH_2048_BIT_MODP{} type DH_2048_BIT_MODP struct { - priority uint32 factor *big.Int generator *big.Int factorBytesLength int } -func (t *DH_2048_BIT_MODP) transformID() uint16 { - return types.DH_2048_BIT_MODP +func (t *DH_2048_BIT_MODP) TransformID() uint16 { + return message.DH_2048_BIT_MODP } func (t *DH_2048_BIT_MODP) getAttribute() (bool, uint16, uint16, []byte) { return false, 0, 0, nil } -func (t *DH_2048_BIT_MODP) setPriority(priority uint32) { - t.priority = priority -} - -func (t *DH_2048_BIT_MODP) Priority() uint32 { - return t.priority -} - func (t *DH_2048_BIT_MODP) GetSharedKey(secret, peerPublicValue *big.Int) []byte { sharedKey := new(big.Int).Exp(peerPublicValue, secret, t.factor).Bytes() prependZero := make([]byte, t.factorBytesLength-len(sharedKey)) diff --git a/security/encr/encr.go b/security/encr/encr.go new file mode 100644 index 0000000..d80981d --- /dev/null +++ b/security/encr/encr.go @@ -0,0 +1,142 @@ +package encr + +import ( + "github.com/pkg/errors" + + "github.com/free5gc/ike/message" + ikeCrypto "github.com/free5gc/ike/security/IKECrypto" +) + +var encrString map[uint16]func(uint16, uint16, []byte) string + +var ( + encrTypes map[string]ENCRType + encrKTypes map[string]ENCRKType +) + +func init() { + // ENCR String + encrString = make(map[uint16]func(uint16, uint16, []byte) string) + encrString[message.ENCR_AES_CBC] = toString_ENCR_AES_CBC + + // ENCR Types + encrTypes = make(map[string]ENCRType) + + encrTypes[string_ENCR_AES_CBC_128] = &ENCR_AES_CBC{ + keyLength: 16, + } + encrTypes[string_ENCR_AES_CBC_192] = &ENCR_AES_CBC{ + keyLength: 24, + } + encrTypes[string_ENCR_AES_CBC_256] = &ENCR_AES_CBC{ + keyLength: 32, + } + + // ENCR Kernel Types + encrKTypes = make(map[string]ENCRKType) + + encrKTypes[string_ENCR_AES_CBC_128] = &ENCR_AES_CBC{ + keyLength: 16, + } + encrKTypes[string_ENCR_AES_CBC_192] = &ENCR_AES_CBC{ + keyLength: 24, + } + encrKTypes[string_ENCR_AES_CBC_256] = &ENCR_AES_CBC{ + keyLength: 32, + } +} + +func StrToType(algo string) ENCRType { + if t, ok := encrTypes[algo]; ok { + return t + } else { + return nil + } +} + +func StrToKType(algo string) ENCRKType { + if t, ok := encrKTypes[algo]; ok { + return t + } else { + return nil + } +} + +func DecodeTransform(transform *message.Transform) ENCRType { + if f, ok := encrString[transform.TransformID]; ok { + s := f(transform.AttributeType, transform.AttributeValue, transform.VariableLengthAttributeValue) + if s != "" { + if encrType, ok2 := encrTypes[s]; ok2 { + return encrType + } else { + return nil + } + } else { + return nil + } + } else { + return nil + } +} + +func ToTransform(encrType ENCRType) (*message.Transform, error) { + t := new(message.Transform) + var err error + t.TransformType = message.TypeEncryptionAlgorithm + t.TransformID = encrType.TransformID() + t.AttributePresent, t.AttributeType, t.AttributeValue, t.VariableLengthAttributeValue, + err = encrType.getAttribute() + if err != nil { + return nil, errors.Wrapf(err, "ToTransform") + } + if t.AttributePresent && t.VariableLengthAttributeValue == nil { + t.AttributeFormat = message.AttributeFormatUseTV + } + return t, nil +} + +func DecodeTransformChildSA(transform *message.Transform) ENCRKType { + if f, ok := encrString[transform.TransformID]; ok { + s := f(transform.AttributeType, transform.AttributeValue, transform.VariableLengthAttributeValue) + if s != "" { + if encrKType, ok2 := encrKTypes[s]; ok2 { + return encrKType + } else { + return nil + } + } else { + return nil + } + } else { + return nil + } +} + +func ToTransformChildSA(encrKType ENCRKType) (*message.Transform, error) { + t := new(message.Transform) + var err error + t.TransformType = message.TypeEncryptionAlgorithm + t.TransformID = encrKType.TransformID() + t.AttributePresent, t.AttributeType, t.AttributeValue, t.VariableLengthAttributeValue, + err = encrKType.getAttribute() + if err != nil { + return nil, errors.Wrapf(err, "ToTransformChildSA") + } + if t.AttributePresent && t.VariableLengthAttributeValue == nil { + t.AttributeFormat = 1 // TV + } + return t, nil +} + +type ENCRType interface { + TransformID() uint16 + getAttribute() (bool, uint16, uint16, []byte, error) + GetKeyLength() int + NewCrypto(key []byte) (ikeCrypto.IKECrypto, error) +} + +type ENCRKType interface { + TransformID() uint16 + getAttribute() (bool, uint16, uint16, []byte, error) + GetKeyLength() int +} diff --git a/internal/encr/encr_aes_cbc.go b/security/encr/encr_aes_cbc.go similarity index 62% rename from internal/encr/encr_aes_cbc.go rename to security/encr/encr_aes_cbc.go index 50f0229..b2d448d 100644 --- a/internal/encr/encr_aes_cbc.go +++ b/security/encr/encr_aes_cbc.go @@ -4,13 +4,13 @@ import ( "crypto/aes" "crypto/cipher" "crypto/rand" - "encoding/hex" - "errors" "io" - "github.com/free5gc/ike/internal/lib" - itypes "github.com/free5gc/ike/internal/types" - types "github.com/free5gc/ike/types" + "github.com/pkg/errors" + + "github.com/free5gc/ike/message" + ikeCrypto "github.com/free5gc/ike/security/IKECrypto" + "github.com/free5gc/ike/security/lib" ) const ( @@ -20,7 +20,7 @@ const ( ) func toString_ENCR_AES_CBC(attrType uint16, intValue uint16, bytesValue []byte) string { - if attrType == types.AttributeTypeKeyLength { + if attrType == message.AttributeTypeKeyLength { switch intValue { case 128: return string_ENCR_AES_CBC_128 @@ -42,49 +42,39 @@ var ( ) type ENCR_AES_CBC struct { - priority uint32 keyLength int } -func (t *ENCR_AES_CBC) transformID() uint16 { - return types.ENCR_AES_CBC -} - -func (t *ENCR_AES_CBC) getAttribute() (bool, uint16, uint16, []byte) { - return true, types.AttributeTypeKeyLength, uint16(t.keyLength * 8), nil -} - -func (t *ENCR_AES_CBC) setPriority(priority uint32) { - t.priority = priority +func (t *ENCR_AES_CBC) TransformID() uint16 { + return message.ENCR_AES_CBC } -func (t *ENCR_AES_CBC) Priority() uint32 { - return t.priority +func (t *ENCR_AES_CBC) getAttribute() (bool, uint16, uint16, []byte, error) { + keyLengthBits := t.keyLength * 8 + if keyLengthBits < 0 || keyLengthBits > 0xFFFF { + return false, 0, 0, nil, errors.Errorf("key length exceeds uint16 maximum value: %v", keyLengthBits) + } + return true, message.AttributeTypeKeyLength, uint16(keyLengthBits), nil, nil } func (t *ENCR_AES_CBC) GetKeyLength() int { return t.keyLength } -func (t *ENCR_AES_CBC) Init(key []byte) itypes.IKECrypto { +func (t *ENCR_AES_CBC) NewCrypto(key []byte) (ikeCrypto.IKECrypto, error) { var err error encr := new(ENCR_AES_CBC_Crypto) if len(key) != t.keyLength { - return nil + return nil, errors.Errorf("ENCR_AES_CBC init error: Get unexpected key length") } if encr.block, err = aes.NewCipher(key); err != nil { - encrLog.Errorf("Error occur when create new cipher: %+v", err) - return nil + return nil, errors.Wrapf(err, "ENCR_AES_CBC init: Error occur when create new cipher: ") } else { - return encr + return encr, nil } } -func (t *ENCR_AES_CBC) XFRMString() string { - return "cbc(aes)" -} - -var _ itypes.IKECrypto = &ENCR_AES_CBC_Crypto{} +var _ ikeCrypto.IKECrypto = &ENCR_AES_CBC_Crypto{} type ENCR_AES_CBC_Crypto struct { block cipher.Block @@ -102,12 +92,11 @@ func (encr *ENCR_AES_CBC_Crypto) Encrypt(plainText []byte) ([]byte, error) { // IV _, err := io.ReadFull(rand.Reader, initializationVector) if err != nil { - encrLog.Errorf("Read random failed: %+v", err) - return nil, errors.New("Read random initialization vector failed") + return nil, errors.Errorf("Read random initialization vector failed") } // Encryption - cbcBlockMode := cipher.NewCBCEncrypter(encr.block, initializationVector) + cbcBlockMode := cipher.NewCBCEncrypter(encr.block, initializationVector) // #nosec G407 cbcBlockMode.CryptBlocks(cipherText[aes.BlockSize:], plainText) return cipherText, nil @@ -116,32 +105,30 @@ func (encr *ENCR_AES_CBC_Crypto) Encrypt(plainText []byte) ([]byte, error) { func (encr *ENCR_AES_CBC_Crypto) Decrypt(cipherText []byte) ([]byte, error) { // Check if len(cipherText) < aes.BlockSize { - encrLog.Error("Length of cipher text is too short to decrypt") - return nil, errors.New("Cipher text is too short") + return nil, errors.Errorf("ENCR_AES_CBC_Crypto: Length of cipher text is too short to decrypt") } initializationVector := cipherText[:aes.BlockSize] encryptedMessage := cipherText[aes.BlockSize:] if len(encryptedMessage)%aes.BlockSize != 0 { - encrLog.Error("Cipher text is not a multiple of block size") - return nil, errors.New("Cipher text length error") + return nil, errors.Errorf("ENCR_AES_CBC_Crypto: Cipher text is not a multiple of block size") } // Slice plainText := make([]byte, len(encryptedMessage)) // Decryption - cbcBlockMode := cipher.NewCBCDecrypter(encr.block, initializationVector) + cbcBlockMode := cipher.NewCBCDecrypter(encr.block, initializationVector) // #nosec G407 cbcBlockMode.CryptBlocks(plainText, encryptedMessage) - encrLog.Tracef("Decrypted content:\n%s", hex.Dump(plainText)) + // fmt.Printf("Decrypted content:\n%s", hex.Dump(plainText)) // Remove padding padding := int(plainText[len(plainText)-1]) + 1 plainText = plainText[:len(plainText)-padding] - encrLog.Tracef("Decrypted content with out padding:\n%s", hex.Dump(plainText)) + // fmt.Printf("Decrypted content with out padding:\n%s", hex.Dump(plainText)) return plainText, nil } diff --git a/security/esn/esn.go b/security/esn/esn.go new file mode 100644 index 0000000..efff931 --- /dev/null +++ b/security/esn/esn.go @@ -0,0 +1,98 @@ +package esn + +import ( + "github.com/pkg/errors" + + "github.com/free5gc/ike/message" +) + +var ( + esnString map[uint16]func(uint16, uint16, []byte) string + esnTypes map[string]ESN +) + +const ( + string_ESN_ENABLE string = "ESN_ENABLE" + string_ESN_DISABLE string = "ESN_DISABLE" +) + +func toString_ESN_ENABLE(attrType uint16, intValue uint16, bytesValue []byte) string { + return string_ESN_ENABLE +} + +func toString_ESN_DISABLE(attrType uint16, intValue uint16, bytesValue []byte) string { + return string_ESN_DISABLE +} + +func init() { + // ESN String + esnString = make(map[uint16]func(uint16, uint16, []byte) string) + esnString[message.ESN_ENABLE] = toString_ESN_ENABLE + esnString[message.ESN_DISABLE] = toString_ESN_DISABLE + + // ESN Types + esnTypes = make(map[string]ESN) + + esnTypes[string_ESN_ENABLE] = ESN{ + needESN: true, + } + esnTypes[string_ESN_DISABLE] = ESN{ + needESN: false, + } +} + +type ESN struct { + needESN bool +} + +func (e *ESN) GetNeedESN() bool { + return e.needESN +} + +func (e *ESN) TransformID() uint16 { + if e.needESN { + return message.ESN_ENABLE + } else { + return message.ESN_DISABLE + } +} + +func (e *ESN) getAttribute() (bool, uint16, uint16, []byte) { + return false, 0, 0, nil +} + +func StrToType(algo string) (ESN, error) { + if t, ok := esnTypes[algo]; ok { + return t, nil + } else { + return ESN{}, errors.Errorf("ESN StrToType get unsupport string") + } +} + +func DecodeTransform(transform *message.Transform) (ESN, error) { + if f, ok := esnString[transform.TransformID]; ok { + s := f(transform.AttributeType, transform.AttributeValue, transform.VariableLengthAttributeValue) + if s != "" { + esn, err := StrToType(s) + if err != nil { + return ESN{}, errors.Wrapf(err, "ESN DecodeTransform") + } + return esn, nil + } else { + return ESN{}, errors.Errorf("ESN DecodeTransform get unsupport string") + } + } else { + return ESN{}, errors.Errorf("ESN DecodeTransform get unsupport transform") + } +} + +func ToTransform(esnType ESN) *message.Transform { + t := new(message.Transform) + t.TransformType = message.TypeExtendedSequenceNumbers + t.TransformID = esnType.TransformID() + t.AttributePresent, t.AttributeType, t.AttributeValue, t.VariableLengthAttributeValue = esnType.getAttribute() + if t.AttributePresent && t.VariableLengthAttributeValue == nil { + t.AttributeFormat = message.AttributeFormatUseTV + } + return t +} diff --git a/internal/integ/auth_hmac_md5_96.go b/security/integ/auth_hmac_md5_96.go similarity index 67% rename from internal/integ/auth_hmac_md5_96.go rename to security/integ/auth_hmac_md5_96.go index 7ba72c1..d868db5 100644 --- a/internal/integ/auth_hmac_md5_96.go +++ b/security/integ/auth_hmac_md5_96.go @@ -2,10 +2,10 @@ package integ import ( "crypto/hmac" - "crypto/md5" + "crypto/md5" // #nosec G501 "hash" - "github.com/free5gc/ike/types" + "github.com/free5gc/ike/message" ) const string_AUTH_HMAC_MD5_96 string = "AUTH_HMAC_MD5_96" @@ -20,27 +20,18 @@ var ( ) type AUTH_HMAC_MD5_96 struct { - priority uint32 keyLength int outputLength int } -func (t *AUTH_HMAC_MD5_96) transformID() uint16 { - return types.AUTH_HMAC_MD5_96 +func (t *AUTH_HMAC_MD5_96) TransformID() uint16 { + return message.AUTH_HMAC_MD5_96 } func (t *AUTH_HMAC_MD5_96) getAttribute() (bool, uint16, uint16, []byte) { return false, 0, 0, nil } -func (t *AUTH_HMAC_MD5_96) setPriority(priority uint32) { - t.priority = priority -} - -func (t *AUTH_HMAC_MD5_96) Priority() uint32 { - return t.priority -} - func (t *AUTH_HMAC_MD5_96) GetKeyLength() int { return t.keyLength } @@ -56,7 +47,3 @@ func (t *AUTH_HMAC_MD5_96) Init(key []byte) hash.Hash { return nil } } - -func (t *AUTH_HMAC_MD5_96) XFRMString() string { - return "hmac(md5)" -} diff --git a/internal/integ/auth_hmac_sha1_96.go b/security/integ/auth_hmac_sha1_96.go similarity index 67% rename from internal/integ/auth_hmac_sha1_96.go rename to security/integ/auth_hmac_sha1_96.go index c88ccc3..ad3a8a7 100644 --- a/internal/integ/auth_hmac_sha1_96.go +++ b/security/integ/auth_hmac_sha1_96.go @@ -2,10 +2,10 @@ package integ import ( "crypto/hmac" - "crypto/sha1" + "crypto/sha1" // #nosec G505 "hash" - "github.com/free5gc/ike/types" + "github.com/free5gc/ike/message" ) const string_AUTH_HMAC_SHA1_96 string = "AUTH_HMAC_SHA1_96" @@ -20,27 +20,18 @@ var ( ) type AUTH_HMAC_SHA1_96 struct { - priority uint32 keyLength int outputLength int } -func (t *AUTH_HMAC_SHA1_96) transformID() uint16 { - return types.AUTH_HMAC_SHA1_96 +func (t *AUTH_HMAC_SHA1_96) TransformID() uint16 { + return message.AUTH_HMAC_SHA1_96 } func (t *AUTH_HMAC_SHA1_96) getAttribute() (bool, uint16, uint16, []byte) { return false, 0, 0, nil } -func (t *AUTH_HMAC_SHA1_96) setPriority(priority uint32) { - t.priority = priority -} - -func (t *AUTH_HMAC_SHA1_96) Priority() uint32 { - return t.priority -} - func (t *AUTH_HMAC_SHA1_96) GetKeyLength() int { return t.keyLength } @@ -56,7 +47,3 @@ func (t *AUTH_HMAC_SHA1_96) Init(key []byte) hash.Hash { return nil } } - -func (t *AUTH_HMAC_SHA1_96) XFRMString() string { - return "hmac(sha1)" -} diff --git a/security/integ/auth_hmac_sha2_256_128.go b/security/integ/auth_hmac_sha2_256_128.go new file mode 100644 index 0000000..d0b3a4d --- /dev/null +++ b/security/integ/auth_hmac_sha2_256_128.go @@ -0,0 +1,49 @@ +package integ + +import ( + "crypto/hmac" + "crypto/sha256" + "hash" + + "github.com/free5gc/ike/message" +) + +const string_AUTH_HMAC_SHA2_256_128 string = "AUTH_HMAC_SHA2_256_128" + +func toString_AUTH_HMAC_SHA2_256_128(attrType uint16, intValue uint16, bytesValue []byte) string { + return string_AUTH_HMAC_SHA2_256_128 +} + +var ( + _ INTEGType = &AUTH_HMAC_SHA2_256_128{} + _ INTEGKType = &AUTH_HMAC_SHA2_256_128{} +) + +type AUTH_HMAC_SHA2_256_128 struct { + keyLength int + outputLength int +} + +func (t *AUTH_HMAC_SHA2_256_128) TransformID() uint16 { + return message.AUTH_HMAC_SHA2_256_128 +} + +func (t *AUTH_HMAC_SHA2_256_128) getAttribute() (bool, uint16, uint16, []byte) { + return false, 0, 0, nil +} + +func (t *AUTH_HMAC_SHA2_256_128) GetKeyLength() int { + return t.keyLength +} + +func (t *AUTH_HMAC_SHA2_256_128) GetOutputLength() int { + return t.outputLength +} + +func (t *AUTH_HMAC_SHA2_256_128) Init(key []byte) hash.Hash { + if len(key) == 32 { + return hmac.New(sha256.New, key) + } else { + return nil + } +} diff --git a/internal/integ/integ.go b/security/integ/integ.go similarity index 51% rename from internal/integ/integ.go rename to security/integ/integ.go index 911efc0..f80f4f8 100644 --- a/internal/integ/integ.go +++ b/security/integ/integ.go @@ -1,20 +1,12 @@ package integ import ( - "errors" "hash" - "github.com/sirupsen/logrus" - - "github.com/free5gc/ike/internal/logger" "github.com/free5gc/ike/message" - "github.com/free5gc/ike/types" ) -var ( - integLog *logrus.Entry - integString map[uint16]func(uint16, uint16, []byte) string -) +var integString map[uint16]func(uint16, uint16, []byte) string var ( integTypes map[string]INTEGType @@ -22,13 +14,11 @@ var ( ) func init() { - // Log - integLog = logger.INTEGLog - // INTEG String integString = make(map[uint16]func(uint16, uint16, []byte) string) - integString[types.AUTH_HMAC_MD5_96] = toString_AUTH_HMAC_MD5_96 - integString[types.AUTH_HMAC_SHA1_96] = toString_AUTH_HMAC_SHA1_96 + integString[message.AUTH_HMAC_MD5_96] = toString_AUTH_HMAC_MD5_96 + integString[message.AUTH_HMAC_SHA1_96] = toString_AUTH_HMAC_SHA1_96 + integString[message.AUTH_HMAC_SHA2_256_128] = toString_AUTH_HMAC_SHA2_256_128 // INTEG Types integTypes = make(map[string]INTEGType) @@ -41,21 +31,9 @@ func init() { keyLength: 20, outputLength: 12, } - - // Default Priority - priority := []string{ - string_AUTH_HMAC_MD5_96, - string_AUTH_HMAC_SHA1_96, - } - - // Set Priority - for i, s := range priority { - if integType, ok := integTypes[s]; ok { - integType.setPriority(uint32(i)) - } else { - integLog.Error("No such INTEG implementation") - panic("IKE INTEG failed to init.") - } + integTypes[string_AUTH_HMAC_SHA2_256_128] = &AUTH_HMAC_SHA2_256_128{ + keyLength: 32, + outputLength: 16, } // INTEG Kernel Types @@ -69,47 +47,12 @@ func init() { keyLength: 20, outputLength: 12, } - - // INTEG Kernel Priority same as above - // Set Priority - for i, s := range priority { - if integKType, ok := integKTypes[s]; ok { - integKType.setPriority(uint32(i)) - } else { - integLog.Error("No such INTEG implementation") - panic("IKE INTEG failed to init.") - } + integKTypes[string_AUTH_HMAC_SHA2_256_128] = &AUTH_HMAC_SHA2_256_128{ + keyLength: 32, + outputLength: 16, } } -func SetPriority(algolist map[string]uint32) error { - // check implemented - for algo := range algolist { - if _, ok := integTypes[algo]; !ok { - return errors.New("No such implementation") - } - } - // set priority - for algo, priority := range algolist { - integTypes[algo].setPriority(priority) - } - return nil -} - -func SetKPriority(algolist []string) error { - // check implemented - for _, algo := range algolist { - if _, ok := integKTypes[algo]; !ok { - return errors.New("No such implementation") - } - } - // set priority - for i, algo := range algolist { - integKTypes[algo].setPriority(uint32(i)) - } - return nil -} - func StrToType(algo string) INTEGType { if t, ok := integTypes[algo]; ok { return t @@ -130,7 +73,7 @@ func DecodeTransform(transform *message.Transform) INTEGType { if f, ok := integString[transform.TransformID]; ok { s := f(transform.AttributeType, transform.AttributeValue, transform.VariableLengthAttributeValue) if s != "" { - if integType, ok := integTypes[s]; ok { + if integType, ok2 := integTypes[s]; ok2 { return integType } else { return nil @@ -145,8 +88,8 @@ func DecodeTransform(transform *message.Transform) INTEGType { func ToTransform(integType INTEGType) *message.Transform { t := new(message.Transform) - t.TransformType = types.TypeIntegrityAlgorithm - t.TransformID = integType.transformID() + t.TransformType = message.TypeIntegrityAlgorithm + t.TransformID = integType.TransformID() t.AttributePresent, t.AttributeType, t.AttributeValue, t.VariableLengthAttributeValue = integType.getAttribute() if t.AttributePresent && t.VariableLengthAttributeValue == nil { t.AttributeFormat = 1 // TV @@ -158,7 +101,7 @@ func DecodeTransformChildSA(transform *message.Transform) INTEGKType { if f, ok := integString[transform.TransformID]; ok { s := f(transform.AttributeType, transform.AttributeValue, transform.VariableLengthAttributeValue) if s != "" { - if integKType, ok := integKTypes[s]; ok { + if integKType, ok2 := integKTypes[s]; ok2 { return integKType } else { return nil @@ -173,47 +116,25 @@ func DecodeTransformChildSA(transform *message.Transform) INTEGKType { func ToTransformChildSA(integKType INTEGKType) *message.Transform { t := new(message.Transform) - t.TransformType = types.TypeIntegrityAlgorithm - t.TransformID = integKType.transformID() + t.TransformType = message.TypeIntegrityAlgorithm + t.TransformID = integKType.TransformID() t.AttributePresent, t.AttributeType, t.AttributeValue, t.VariableLengthAttributeValue = integKType.getAttribute() if t.AttributePresent && t.VariableLengthAttributeValue == nil { - t.AttributeFormat = types.AttributeFormatUseTV + t.AttributeFormat = message.AttributeFormatUseTV } return t } type INTEGType interface { - transformID() uint16 + TransformID() uint16 getAttribute() (bool, uint16, uint16, []byte) - setPriority(uint32) - Priority() uint32 GetKeyLength() int GetOutputLength() int Init(key []byte) hash.Hash } type INTEGKType interface { - transformID() uint16 + TransformID() uint16 getAttribute() (bool, uint16, uint16, []byte) - setPriority(uint32) - Priority() uint32 GetKeyLength() int - XFRMString() string -} - -/* Archive for future use -type XFRMIntegrityAlgorithmType uint16 - -func (xfrmIntegrityAlgorithmType XFRMIntegrityAlgorithmType) String() string { - switch xfrmIntegrityAlgorithmType { - case message.AUTH_HMAC_MD5_96: - return "hmac(md5)" - case message.AUTH_HMAC_SHA1_96: - return "hmac(sha1)" - case message.AUTH_AES_XCBC_96: - return "xcbc(aes)" - default: - return "" - } } -*/ diff --git a/internal/lib/lib.go b/security/lib/lib.go similarity index 100% rename from internal/lib/lib.go rename to security/lib/lib.go diff --git a/internal/prf/prf.go b/security/prf/prf.go similarity index 54% rename from internal/prf/prf.go rename to security/prf/prf.go index 3d97c7d..c52fdbc 100644 --- a/internal/prf/prf.go +++ b/security/prf/prf.go @@ -1,30 +1,22 @@ package prf import ( - "errors" "hash" - "github.com/sirupsen/logrus" - - "github.com/free5gc/ike/internal/logger" "github.com/free5gc/ike/message" - "github.com/free5gc/ike/types" ) var ( - prfLog *logrus.Entry prfString map[uint16]func(uint16, uint16, []byte) string prfTypes map[string]PRFType ) func init() { - // Log - prfLog = logger.PRFLog - // PRF String prfString = make(map[uint16]func(uint16, uint16, []byte) string) - prfString[types.PRF_HMAC_MD5] = toString_PRF_HMAC_MD5 - prfString[types.PRF_HMAC_SHA1] = toString_PRF_HMAC_SHA1 + prfString[message.PRF_HMAC_MD5] = toString_PRF_HMAC_MD5 + prfString[message.PRF_HMAC_SHA1] = toString_PRF_HMAC_SHA1 + prfString[message.PRF_HMAC_SHA2_256] = toString_PRF_HMAC_SHA2_256 // PRF Types prfTypes = make(map[string]PRFType) @@ -37,36 +29,10 @@ func init() { keyLength: 20, outputLength: 20, } - - // Default Priority - priority := []string{ - string_PRF_HMAC_MD5, - string_PRF_HMAC_SHA1, - } - - // Set Priority - for i, s := range priority { - if prfType, ok := prfTypes[s]; ok { - prfType.setPriority(uint32(i)) - } else { - prfLog.Error("No such PRF implementation") - panic("IKE PRF failed to init.") - } - } -} - -func SetPriority(algolist map[string]uint32) error { - // check implemented - for algo := range algolist { - if _, ok := prfTypes[algo]; !ok { - return errors.New("No such implementation") - } - } - // set priority - for algo, priority := range algolist { - prfTypes[algo].setPriority(priority) + prfTypes[string_PRF_HMAC_SHA2_256] = &PRF_HMAC_SHA2_256{ + keyLength: 32, + outputLength: 32, } - return nil } func StrToType(algo string) PRFType { @@ -81,7 +47,7 @@ func DecodeTransform(transform *message.Transform) PRFType { if f, ok := prfString[transform.TransformID]; ok { s := f(transform.AttributeType, transform.AttributeValue, transform.VariableLengthAttributeValue) if s != "" { - if prfType, ok := prfTypes[s]; ok { + if prfType, ok2 := prfTypes[s]; ok2 { return prfType } else { return nil @@ -96,20 +62,18 @@ func DecodeTransform(transform *message.Transform) PRFType { func ToTransform(prfType PRFType) *message.Transform { t := new(message.Transform) - t.TransformType = types.TypePseudorandomFunction - t.TransformID = prfType.transformID() + t.TransformType = message.TypePseudorandomFunction + t.TransformID = prfType.TransformID() t.AttributePresent, t.AttributeType, t.AttributeValue, t.VariableLengthAttributeValue = prfType.getAttribute() if t.AttributePresent && t.VariableLengthAttributeValue == nil { - t.AttributeFormat = types.AttributeFormatUseTV + t.AttributeFormat = message.AttributeFormatUseTV } return t } type PRFType interface { - transformID() uint16 + TransformID() uint16 getAttribute() (bool, uint16, uint16, []byte) - setPriority(uint32) - Priority() uint32 GetKeyLength() int GetOutputLength() int Init(key []byte) hash.Hash diff --git a/internal/prf/prf_hmac_md5.go b/security/prf/prf_hmac_md5.go similarity index 69% rename from internal/prf/prf_hmac_md5.go rename to security/prf/prf_hmac_md5.go index 427c131..6fab236 100644 --- a/internal/prf/prf_hmac_md5.go +++ b/security/prf/prf_hmac_md5.go @@ -2,10 +2,10 @@ package prf import ( "crypto/hmac" - "crypto/md5" + "crypto/md5" // #nosec G501 "hash" - "github.com/free5gc/ike/types" + "github.com/free5gc/ike/message" ) const string_PRF_HMAC_MD5 string = "PRF_HMAC_MD5" @@ -17,27 +17,18 @@ func toString_PRF_HMAC_MD5(attrType uint16, intValue uint16, bytesValue []byte) var _ PRFType = &PRF_HMAC_MD5{} type PRF_HMAC_MD5 struct { - priority uint32 keyLength int outputLength int } -func (t *PRF_HMAC_MD5) transformID() uint16 { - return types.PRF_HMAC_MD5 +func (t *PRF_HMAC_MD5) TransformID() uint16 { + return message.PRF_HMAC_MD5 } func (t *PRF_HMAC_MD5) getAttribute() (bool, uint16, uint16, []byte) { return false, 0, 0, nil } -func (t *PRF_HMAC_MD5) setPriority(priority uint32) { - t.priority = priority -} - -func (t *PRF_HMAC_MD5) Priority() uint32 { - return t.priority -} - func (t *PRF_HMAC_MD5) GetKeyLength() int { return t.keyLength } diff --git a/internal/prf/prf_hmac_sha1.go b/security/prf/prf_hmac_sha1.go similarity index 69% rename from internal/prf/prf_hmac_sha1.go rename to security/prf/prf_hmac_sha1.go index 8b793b1..ddd0198 100644 --- a/internal/prf/prf_hmac_sha1.go +++ b/security/prf/prf_hmac_sha1.go @@ -2,10 +2,10 @@ package prf import ( "crypto/hmac" - "crypto/sha1" + "crypto/sha1" // #nosec G505 "hash" - "github.com/free5gc/ike/types" + "github.com/free5gc/ike/message" ) const string_PRF_HMAC_SHA1 string = "PRF_HMAC_SHA1" @@ -17,27 +17,18 @@ func toString_PRF_HMAC_SHA1(attrType uint16, intValue uint16, bytesValue []byte) var _ PRFType = &PRF_HMAC_SHA1{} type PRF_HMAC_SHA1 struct { - priority uint32 keyLength int outputLength int } -func (t *PRF_HMAC_SHA1) transformID() uint16 { - return types.PRF_HMAC_SHA1 +func (t *PRF_HMAC_SHA1) TransformID() uint16 { + return message.PRF_HMAC_SHA1 } func (t *PRF_HMAC_SHA1) getAttribute() (bool, uint16, uint16, []byte) { return false, 0, 0, nil } -func (t *PRF_HMAC_SHA1) setPriority(priority uint32) { - t.priority = priority -} - -func (t *PRF_HMAC_SHA1) Priority() uint32 { - return t.priority -} - func (t *PRF_HMAC_SHA1) GetKeyLength() int { return t.keyLength } diff --git a/security/prf/prf_hmac_sha2_256.go b/security/prf/prf_hmac_sha2_256.go new file mode 100644 index 0000000..c26d2d7 --- /dev/null +++ b/security/prf/prf_hmac_sha2_256.go @@ -0,0 +1,42 @@ +package prf + +import ( + "crypto/hmac" + "crypto/sha256" + "hash" + + "github.com/free5gc/ike/message" +) + +const string_PRF_HMAC_SHA2_256 string = "PRF_HMAC_SHA2_256" + +func toString_PRF_HMAC_SHA2_256(attrType uint16, intValue uint16, bytesValue []byte) string { + return string_PRF_HMAC_SHA2_256 +} + +var _ PRFType = &PRF_HMAC_SHA2_256{} + +type PRF_HMAC_SHA2_256 struct { + keyLength int + outputLength int +} + +func (t *PRF_HMAC_SHA2_256) TransformID() uint16 { + return message.PRF_HMAC_SHA2_256 +} + +func (t *PRF_HMAC_SHA2_256) getAttribute() (bool, uint16, uint16, []byte) { + return false, 0, 0, nil +} + +func (t *PRF_HMAC_SHA2_256) GetKeyLength() int { + return t.keyLength +} + +func (t *PRF_HMAC_SHA2_256) GetOutputLength() int { + return t.outputLength +} + +func (t *PRF_HMAC_SHA2_256) Init(key []byte) hash.Hash { + return hmac.New(sha256.New, key) +} diff --git a/security/security.go b/security/security.go index 73f2b33..c817bdd 100644 --- a/security/security.go +++ b/security/security.go @@ -1,36 +1,28 @@ package security import ( - "crypto/hmac" + "bytes" "crypto/rand" "encoding/binary" "encoding/hex" - "errors" - "fmt" "hash" "io" "math/big" - "net" + "strconv" "strings" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" - - "github.com/free5gc/ike/internal/dh" - "github.com/free5gc/ike/internal/encr" - "github.com/free5gc/ike/internal/esn" - "github.com/free5gc/ike/internal/integ" - "github.com/free5gc/ike/internal/lib" - "github.com/free5gc/ike/internal/logger" - "github.com/free5gc/ike/internal/prf" - itypes "github.com/free5gc/ike/internal/types" + "github.com/pkg/errors" + "github.com/free5gc/ike/message" - types "github.com/free5gc/ike/types" + ikeCrypto "github.com/free5gc/ike/security/IKECrypto" + "github.com/free5gc/ike/security/dh" + "github.com/free5gc/ike/security/encr" + "github.com/free5gc/ike/security/esn" + "github.com/free5gc/ike/security/integ" + "github.com/free5gc/ike/security/lib" + "github.com/free5gc/ike/security/prf" ) -// Log -var secLog *logrus.Entry - // General data var ( randomNumberMaximum big.Int @@ -38,1150 +30,439 @@ var ( ) func init() { - // Log - secLog = logger.SecLog // General data randomNumberMaximum.SetString(strings.Repeat("F", 512), 16) randomNumberMinimum.SetString(strings.Repeat("F", 32), 16) } -func GenerateRandomNumber() *big.Int { +func GenerateRandomNumber() (*big.Int, error) { var number *big.Int var err error for { number, err = rand.Int(rand.Reader, &randomNumberMaximum) if err != nil { - secLog.Errorf("Error occurs when generate random number: %+v", err) - return nil - } else { - if number.Cmp(&randomNumberMinimum) == 1 { - break - } + return nil, errors.Errorf("GenerateRandomNumber(): Error occurs when generate random number: %+v", err) + } else if number.Cmp(&randomNumberMinimum) == 1 { + break } } - return number + return number, nil } func GenerateRandomUint8() (uint8, error) { number := make([]byte, 1) _, err := io.ReadFull(rand.Reader, number) if err != nil { - secLog.Errorf("Read random failed: %+v", err) - return 0, errors.New("Read failed") + return 0, errors.Errorf("Read random failed: %+v", err) } return number[0], nil } -func concatenateNonceAndSPI(nonce []byte, SPI_initiator uint64, SPI_responder uint64) []byte { +func concatenateNonceAndSPI(nonce []byte, spi_initiator uint64, spi_responder uint64) []byte { + var newSlice []byte spi := make([]byte, 8) - binary.BigEndian.PutUint64(spi, SPI_initiator) - newSlice := append(nonce, spi...) - binary.BigEndian.PutUint64(spi, SPI_responder) + newSlice = append(newSlice, nonce...) + binary.BigEndian.PutUint64(spi, spi_initiator) + newSlice = append(newSlice, spi...) + binary.BigEndian.PutUint64(spi, spi_responder) newSlice = append(newSlice, spi...) return newSlice } -type IKESA struct { - // SPI - RemoteSPI uint64 - LocalSPI uint64 - +type IKESAKey struct { // IKE SA transform types - dhInfo dh.DHType - encrInfo encr.ENCRType - integInfo integ.INTEGType - prfInfo prf.PRFType + DhInfo dh.DHType + EncrInfo encr.ENCRType + IntegInfo integ.INTEGType + PrfInfo prf.PRFType // Security objects - Prf_d hash.Hash // used to derive key for child sa - Integ_i hash.Hash // used by initiator for integrity checking - Integ_r hash.Hash // used by responder for integrity checking - Encr_i itypes.IKECrypto // used by initiator for encrypting - Encr_r itypes.IKECrypto // used by responder for encrypting - Prf_i hash.Hash // used by initiator for IKE authentication - Prf_r hash.Hash // used by responder for IKE authentication + Prf_d hash.Hash // used to derive key for child sa + Integ_i hash.Hash // used by initiator for integrity checking + Integ_r hash.Hash // used by responder for integrity checking + Encr_i ikeCrypto.IKECrypto // used by initiator for encrypting + Encr_r ikeCrypto.IKECrypto // used by responder for encrypting + Prf_i hash.Hash // used by initiator for IKE authentication + Prf_r hash.Hash // used by responder for IKE authentication + + // Keys + SK_d []byte // used for child SA key deriving + SK_ai []byte // used by initiator for integrity checking + SK_ar []byte // used by responder for integrity checking + SK_ei []byte // used by initiator for encrypting + SK_er []byte // used by responder for encrypting + SK_pi []byte // used by initiator for IKE authentication + SK_pr []byte // used by responder for IKE authentication } -func (ikesa *IKESA) SelectProposal(proposal *message.Proposal) bool { - for _, transform := range proposal.DiffieHellmanGroup { - dhType := dh.DecodeTransform(transform) - if dhType != nil { - if ikesa.dhInfo == nil { - ikesa.dhInfo = dhType - } else { - if dhType.Priority() > ikesa.dhInfo.Priority() { - ikesa.dhInfo = dhType - } - } - } - } - if ikesa.dhInfo == nil { - return false // mandatory +func (ikesaKey *IKESAKey) String() string { + return "\nEncryption Algorithm: " + + strconv.FormatUint(uint64(ikesaKey.EncrInfo.TransformID()), 10) + + "\nSK_ei: " + hex.EncodeToString(ikesaKey.SK_ei) + + "\nSK_er: " + hex.EncodeToString(ikesaKey.SK_er) + + "\nIntegrity Algorithm: " + + strconv.FormatUint(uint64(ikesaKey.IntegInfo.TransformID()), 10) + + "\nSK_ai: " + hex.EncodeToString(ikesaKey.SK_ai) + + "\nSK_ar: " + hex.EncodeToString(ikesaKey.SK_ar) + + "\nSK_pi: " + hex.EncodeToString(ikesaKey.SK_pi) + + "\nSK_pr: " + hex.EncodeToString(ikesaKey.SK_pr) + + "\nSK_d : " + hex.EncodeToString(ikesaKey.SK_d) + "\n" +} + +func (ikesaKey *IKESAKey) ToProposal() (*message.Proposal, error) { + p := new(message.Proposal) + p.ProtocolID = message.TypeIKE + p.DiffieHellmanGroup = append(p.DiffieHellmanGroup, dh.ToTransform(ikesaKey.DhInfo)) + p.PseudorandomFunction = append(p.PseudorandomFunction, prf.ToTransform(ikesaKey.PrfInfo)) + encrTranform, err := encr.ToTransform(ikesaKey.EncrInfo) + if err != nil { + return nil, errors.Wrapf(err, "IKESAKey ToProposal") } - for _, transform := range proposal.EncryptionAlgorithm { - encrType := encr.DecodeTransform(transform) - if encrType != nil { - if ikesa.encrInfo == nil { - ikesa.encrInfo = encrType - } else { - if encrType.Priority() > ikesa.encrInfo.Priority() { - ikesa.encrInfo = encrType - } - } - } + p.EncryptionAlgorithm = append(p.EncryptionAlgorithm, encrTranform) + p.IntegrityAlgorithm = append(p.IntegrityAlgorithm, integ.ToTransform(ikesaKey.IntegInfo)) + return p, nil +} + +// return IKESAKey and local public value +func NewIKESAKey( + proposal *message.Proposal, + keyExchangeData, concatenatedNonce []byte, + initiatorSPI, responderSPI uint64, +) (*IKESAKey, []byte, error) { + if proposal == nil { + return nil, nil, errors.Errorf("NewIKESAKey : proposal is nil") } - if ikesa.encrInfo == nil { - return false // mandatory + if len(proposal.DiffieHellmanGroup) == 0 { + return nil, nil, errors.Errorf("NewIKESAKey : DiffieHellmanGroup is nil") } - for _, transform := range proposal.IntegrityAlgorithm { - integType := integ.DecodeTransform(transform) - if integType != nil { - if ikesa.integInfo == nil { - ikesa.integInfo = integType - } else { - if integType.Priority() > ikesa.integInfo.Priority() { - ikesa.integInfo = integType - } - } - } - } - if ikesa.integInfo == nil { - return false // mandatory + + if len(proposal.EncryptionAlgorithm) == 0 { + return nil, nil, errors.Errorf("NewIKESAKey : EncryptionAlgorithm is nil") } - for _, transform := range proposal.PseudorandomFunction { - prfType := prf.DecodeTransform(transform) - if prfType != nil { - if ikesa.prfInfo == nil { - ikesa.prfInfo = prfType - } else { - if prfType.Priority() > ikesa.prfInfo.Priority() { - ikesa.prfInfo = prfType - } - } - } + + if len(proposal.IntegrityAlgorithm) == 0 { + return nil, nil, errors.Errorf("NewIKESAKey : IntegrityAlgorithm is nil") } - if ikesa.prfInfo == nil { - return false // mandatory + + if len(proposal.PseudorandomFunction) == 0 { + return nil, nil, errors.Errorf("NewIKESAKey : PseudorandomFunction is nil") } - if len(proposal.ExtendedSequenceNumbers) > 0 { - return false // No ESN + + ikesaKey := new(IKESAKey) + ikesaKey.DhInfo = dh.DecodeTransform(proposal.DiffieHellmanGroup[0]) + if ikesaKey.DhInfo == nil { + return nil, nil, errors.Errorf("NewIKESAKey : Get unsupport DiffieHellmanGroup[%v]", + proposal.DiffieHellmanGroup[0].TransformID) } - return true -} -func (ikesa *IKESA) ToProposal() *message.Proposal { - p := new(message.Proposal) - p.ProtocolID = types.TypeIKE - p.DiffieHellmanGroup = append(p.DiffieHellmanGroup, dh.ToTransform(ikesa.dhInfo)) - p.PseudorandomFunction = append(p.PseudorandomFunction, prf.ToTransform(ikesa.prfInfo)) - p.EncryptionAlgorithm = append(p.EncryptionAlgorithm, encr.ToTransform(ikesa.encrInfo)) - p.IntegrityAlgorithm = append(p.IntegrityAlgorithm, integ.ToTransform(ikesa.integInfo)) - return p -} + ikesaKey.EncrInfo = encr.DecodeTransform(proposal.EncryptionAlgorithm[0]) + if ikesaKey.EncrInfo == nil { + return nil, nil, errors.Errorf("NewIKESAKey : Get unsupport EncryptionAlgorithm[%v]", + proposal.EncryptionAlgorithm[0].TransformID) + } -func (ikesa *IKESA) SetProposal(proposal *message.Proposal) bool { - if ikesa.dhInfo = dh.DecodeTransform(proposal.DiffieHellmanGroup[0]); ikesa.dhInfo == nil { - return false + ikesaKey.IntegInfo = integ.DecodeTransform(proposal.IntegrityAlgorithm[0]) + if ikesaKey.EncrInfo == nil { + return nil, nil, errors.Errorf("NewIKESAKey : Get unsupport IntegrityAlgorithm[%v]", + proposal.IntegrityAlgorithm[0].TransformID) } - if ikesa.encrInfo = encr.DecodeTransform(proposal.EncryptionAlgorithm[0]); ikesa.encrInfo == nil { - return false + + ikesaKey.PrfInfo = prf.DecodeTransform(proposal.PseudorandomFunction[0]) + if ikesaKey.PrfInfo == nil { + return nil, nil, errors.Errorf("NewIKESAKey : Get unsupport PseudorandomFunction[%v]", + proposal.PseudorandomFunction[0].TransformID) } - if ikesa.integInfo = integ.DecodeTransform(proposal.IntegrityAlgorithm[0]); ikesa.encrInfo == nil { - return false + + localPublicValue, sharedKeyData, err := CalculateDiffieHellmanMaterials( + ikesaKey, keyExchangeData) + if err != nil { + return nil, nil, errors.Wrapf(err, "NewIKESAKey") } - if ikesa.prfInfo = prf.DecodeTransform(proposal.PseudorandomFunction[0]); ikesa.prfInfo == nil { - return false + + err = ikesaKey.GenerateKeyForIKESA(concatenatedNonce, sharedKeyData, + initiatorSPI, responderSPI) + if err != nil { + return nil, nil, errors.Wrapf(err, "NewIKESAKey") } - return true + + return ikesaKey, localPublicValue, nil } -// CalcKEMaterial generates secret and calculate Diffie-Hellman public key +// CalculateDiffieHellmanMaterials generates secret and calculate Diffie-Hellman public key // exchange material. // Peer public value as parameter, return local public value and shared key. -func (ikesa *IKESA) CalcKEMaterial(peerPublicValue []byte) ([]byte, []byte) { - secret := GenerateRandomNumber() +func CalculateDiffieHellmanMaterials( + ikesaKey *IKESAKey, + peerPublicValue []byte, +) ([]byte, []byte, error) { + secret, err := GenerateRandomNumber() + if err != nil { + return nil, nil, errors.Wrapf(err, "CalculateDiffieHellmanMaterials()") + } + peerPublicValueBig := new(big.Int).SetBytes(peerPublicValue) - return ikesa.dhInfo.GetPublicValue(secret), ikesa.dhInfo.GetSharedKey(secret, peerPublicValueBig) + return ikesaKey.DhInfo.GetPublicValue(secret), ikesaKey.DhInfo.GetSharedKey(secret, peerPublicValueBig), nil } -func (ikesa *IKESA) GenerateKey(concatenatedNonce, dhSharedKey []byte) error { +func (ikesaKey *IKESAKey) GenerateKeyForIKESA( + concatenatedNonce, diffieHellmanSharedKey []byte, + initiatorSPI, responderSPI uint64, +) error { // Check parameters - if ikesa == nil { - return errors.New("IKE SA is nil") + if ikesaKey == nil { + return errors.Errorf("IKE SA is nil") } // Check if the context contain needed data - if ikesa.encrInfo == nil { - return errors.New("No encryption algorithm specified") + if ikesaKey.EncrInfo == nil { + return errors.Errorf("No encryption algorithm specified") } - if ikesa.integInfo == nil { - return errors.New("No integrity algorithm specified") + if ikesaKey.IntegInfo == nil { + return errors.Errorf("No integrity algorithm specified") } - if ikesa.prfInfo == nil { - return errors.New("No pseudorandom function specified") + if ikesaKey.PrfInfo == nil { + return errors.Errorf("No pseudorandom function specified") } - if ikesa.dhInfo == nil { - return errors.New("No Diffie-hellman group algorithm specified") + if ikesaKey.DhInfo == nil { + return errors.Errorf("No Diffie-hellman group algorithm specified") } if len(concatenatedNonce) == 0 { - return errors.New("No concatenated nonce data") + return errors.Errorf("No concatenated nonce data") } - if len(dhSharedKey) == 0 { - return errors.New("No Diffie-Hellman shared key") + if len(diffieHellmanSharedKey) == 0 { + return errors.Errorf("No Diffie-Hellman shared key") } // Get key length of SK_d, SK_ai, SK_ar, SK_ei, SK_er, SK_pi, SK_pr var length_SK_d, length_SK_ai, length_SK_ar, length_SK_ei, length_SK_er, length_SK_pi, length_SK_pr, totalKeyLength int - length_SK_d = ikesa.prfInfo.GetKeyLength() - length_SK_ai = ikesa.integInfo.GetKeyLength() + length_SK_d = ikesaKey.PrfInfo.GetKeyLength() + length_SK_ai = ikesaKey.IntegInfo.GetKeyLength() length_SK_ar = length_SK_ai - length_SK_ei = ikesa.encrInfo.GetKeyLength() + length_SK_ei = ikesaKey.EncrInfo.GetKeyLength() length_SK_er = length_SK_ei length_SK_pi, length_SK_pr = length_SK_d, length_SK_d totalKeyLength = length_SK_d + length_SK_ai + length_SK_ar + length_SK_ei + length_SK_er + length_SK_pi + length_SK_pr // Generate IKE SA key as defined in RFC7296 Section 1.3 and Section 1.4 - secLog.Tracef("Concatenated nonce:\n%s", hex.Dump(concatenatedNonce)) - secLog.Tracef("DH shared key:\n%s", hex.Dump(dhSharedKey)) + // fmt.Printf("Concatenated nonce:\n%s", hex.Dump(concatenatedNonce)) + // fmt.Printf("DH shared key:\n%s", hex.Dump(diffieHellmanSharedKey)) - prf := ikesa.prfInfo.Init(concatenatedNonce) - if _, err := prf.Write(dhSharedKey); err != nil { + prf := ikesaKey.PrfInfo.Init(concatenatedNonce) + if _, err := prf.Write(diffieHellmanSharedKey); err != nil { return err } skeyseed := prf.Sum(nil) - seed := concatenateNonceAndSPI(concatenatedNonce, ikesa.RemoteSPI, ikesa.LocalSPI) + seed := concatenateNonceAndSPI(concatenatedNonce, initiatorSPI, responderSPI) - secLog.Tracef("SKEYSEED:\n%s", hex.Dump(skeyseed)) + // fmt.Printf("SKEYSEED:\n%s", hex.Dump(skeyseed)) - keyStream := lib.PrfPlus(ikesa.prfInfo.Init(skeyseed), seed, totalKeyLength) + keyStream := lib.PrfPlus(ikesaKey.PrfInfo.Init(skeyseed), seed, totalKeyLength) if keyStream == nil { - return errors.New("Error happened in PrfPlus") + return errors.Errorf("Error happened in PrfPlus") } // Assign keys into context - sk_d := keyStream[:length_SK_d] + ikesaKey.SK_d = keyStream[:length_SK_d] keyStream = keyStream[length_SK_d:] - sk_ai := keyStream[:length_SK_ai] + ikesaKey.SK_ai = keyStream[:length_SK_ai] keyStream = keyStream[length_SK_ai:] - sk_ar := keyStream[:length_SK_ar] + ikesaKey.SK_ar = keyStream[:length_SK_ar] keyStream = keyStream[length_SK_ar:] - sk_ei := keyStream[:length_SK_ei] + ikesaKey.SK_ei = keyStream[:length_SK_ei] keyStream = keyStream[length_SK_ei:] - sk_er := keyStream[:length_SK_er] + ikesaKey.SK_er = keyStream[:length_SK_er] keyStream = keyStream[length_SK_er:] - sk_pi := keyStream[:length_SK_pi] + ikesaKey.SK_pi = keyStream[:length_SK_pi] keyStream = keyStream[length_SK_pi:] - sk_pr := keyStream[:length_SK_pr] - - secLog.Tracef("SK_d:\n%s", hex.Dump(sk_d)) - secLog.Tracef("SK_ai:\n%s", hex.Dump(sk_ai)) - secLog.Tracef("SK_ar:\n%s", hex.Dump(sk_ar)) - secLog.Tracef("SK_ei:\n%s", hex.Dump(sk_ei)) - secLog.Tracef("SK_er:\n%s", hex.Dump(sk_er)) - secLog.Tracef("SK_pi:\n%s", hex.Dump(sk_pi)) - secLog.Tracef("SK_pr:\n%s", hex.Dump(sk_pr)) + ikesaKey.SK_pr = keyStream[:length_SK_pr] // Set security objects - ikesa.Prf_d = ikesa.prfInfo.Init(sk_d) - ikesa.Integ_i = ikesa.integInfo.Init(sk_ai) - ikesa.Integ_r = ikesa.integInfo.Init(sk_ar) - ikesa.Encr_i = ikesa.encrInfo.Init(sk_ei) - ikesa.Encr_r = ikesa.encrInfo.Init(sk_er) - ikesa.Prf_i = ikesa.prfInfo.Init(sk_pi) - ikesa.Prf_r = ikesa.prfInfo.Init(sk_pr) + ikesaKey.Prf_d = ikesaKey.PrfInfo.Init(ikesaKey.SK_d) + ikesaKey.Integ_i = ikesaKey.IntegInfo.Init(ikesaKey.SK_ai) + ikesaKey.Integ_r = ikesaKey.IntegInfo.Init(ikesaKey.SK_ar) - return nil -} - -func (ikesa *IKESA) VerifyIKEChecksum(role int, data []byte) bool { - checksumLen := ikesa.integInfo.GetOutputLength() - if len(data) <= checksumLen { - return false - } - - checkedData := data[:len(data)-checksumLen] - checksum := data[len(data)-checksumLen:] - - // Calculate - var calculatedChecksum []byte - if role == types.Role_Initiator { - ikesa.Integ_i.Reset() - if _, err := ikesa.Integ_i.Write(checkedData); err != nil { - secLog.Errorf("VerifyIKEChecksum(%d): %+v", role, err) - return false - } - calculatedChecksum = ikesa.Integ_i.Sum(nil) - } else { - ikesa.Integ_r.Reset() - if _, err := ikesa.Integ_r.Write(checkedData); err != nil { - secLog.Errorf("VerifyIKEChecksum(%d): %+v", role, err) - return false - } - calculatedChecksum = ikesa.Integ_r.Sum(nil) - } - - return hmac.Equal(checksum, calculatedChecksum) -} - -func (ikesa *IKESA) CalcIKEChecksum(role int, data []byte) error { - checksumLen := ikesa.integInfo.GetOutputLength() - if len(data) <= checksumLen { - return errors.New("Input data too short") + var err error + ikesaKey.Encr_i, err = ikesaKey.EncrInfo.NewCrypto(ikesaKey.SK_ei) + if err != nil { + return err } - checkedData := data[:len(data)-checksumLen] - checksum := data[len(data)-checksumLen:] - - // Calculate - var calculatedChecksum []byte - if role == types.Role_Initiator { - ikesa.Integ_i.Reset() - if _, err := ikesa.Integ_i.Write(checkedData); err != nil { - return fmt.Errorf("CalcIKEChecksum(%d): %+v", role, err) - } - calculatedChecksum = ikesa.Integ_i.Sum(nil) - } else { - ikesa.Integ_r.Reset() - if _, err := ikesa.Integ_r.Write(checkedData); err != nil { - return fmt.Errorf("CalcIKEChecksum(%d): %+v", role, err) - } - calculatedChecksum = ikesa.Integ_r.Sum(nil) + ikesaKey.Encr_r, err = ikesaKey.EncrInfo.NewCrypto(ikesaKey.SK_er) + if err != nil { + return err } - copy(checksum, calculatedChecksum) + ikesaKey.Prf_i = ikesaKey.PrfInfo.Init(ikesaKey.SK_pi) + ikesaKey.Prf_r = ikesaKey.PrfInfo.Init(ikesaKey.SK_pr) return nil } -func (ikesa *IKESA) EncryptToSKPayload(role int, data []byte) ([]byte, error) { - // Encrypt - var cipherText []byte - if role == types.Role_Initiator { - var err error - if cipherText, err = ikesa.Encr_i.Encrypt(data); err != nil { - secLog.Errorf("Encrypt() failed: %+v", err) - return nil, errors.New("Failed to encrypt to SK") - } - } else { - var err error - if cipherText, err = ikesa.Encr_r.Encrypt(data); err != nil { - secLog.Errorf("Encrypt() failed: %+v", err) - return nil, errors.New("Failed to encrypt to SK") - } - } - - // Append checksum field - checksumField := make([]byte, ikesa.integInfo.GetOutputLength()) - cipherText = append(cipherText, checksumField...) - - return cipherText, nil -} - -func (ikesa *IKESA) DecryptSKPayload(role int, data []byte) ([]byte, error) { - // Delete checksum field - checksumLen := ikesa.integInfo.GetOutputLength() - data = data[:len(data)-checksumLen] - - // Decrypt - var plainText []byte - if role == types.Role_Initiator { - var err error - if plainText, err = ikesa.Encr_i.Decrypt(data); err != nil { - secLog.Errorf("Decrypt() failed: %+v", err) - return nil, errors.New("Failed to decrypt SK") - } - } else { - var err error - if plainText, err = ikesa.Encr_r.Decrypt(data); err != nil { - secLog.Errorf("Decrypt() failed: %+v", err) - return nil, errors.New("Failed to decrypt SK") - } - } - - return plainText, nil -} - -type ChildSA struct { +type ChildSAKey struct { // SPI SPI uint32 // Child SA transform types - dhInfo dh.DHType - encrKInfo encr.ENCRKType - integKInfo integ.INTEGKType - esnInfo esn.ESNType - - // Mark - Mark uint32 - - // IP addresses - RemotePublicIPAddr net.IP - LocalPublicIPAddr net.IP - - // Traffic - IPProto uint8 - TSLocal *net.IPNet - TSRemote *net.IPNet + DhInfo dh.DHType + EncrKInfo encr.ENCRKType + IntegKInfo integ.INTEGKType + EsnInfo esn.ESN // Security - initiatorToResponderEncrKey []byte - responderToInitiatorEncrKey []byte - initiatorToResponderIntegKey []byte - responderToInitiatorIntegKey []byte - - // Encapsulate - EnableEncap bool - LocalPort int - RemotePort int - - // XFRM contexts - initiatorToResponderPolicy *netlink.XfrmPolicy - initiatorToResponderState *netlink.XfrmState - responderToInitiatorPolicy *netlink.XfrmPolicy - responderToInitiatorState *netlink.XfrmState + InitiatorToResponderEncryptionKey []byte + ResponderToInitiatorEncryptionKey []byte + InitiatorToResponderIntegrityKey []byte + ResponderToInitiatorIntegrityKey []byte } -func (childsa *ChildSA) SelectProposal(proposal *message.Proposal) bool { - // DH is optional - for _, transform := range proposal.DiffieHellmanGroup { - dhType := dh.DecodeTransform(transform) - if dhType != nil { - if childsa.dhInfo == nil { - childsa.dhInfo = dhType - } else { - if dhType.Priority() > childsa.dhInfo.Priority() { - childsa.dhInfo = dhType - } - } - } - } - for _, transform := range proposal.EncryptionAlgorithm { - encrKType := encr.DecodeTransformChildSA(transform) - if encrKType != nil { - if childsa.encrKInfo == nil { - childsa.encrKInfo = encrKType - } else { - if encrKType.Priority() > childsa.encrKInfo.Priority() { - childsa.encrKInfo = encrKType - } - } - } - } - if childsa.encrKInfo == nil { - return false // mandatory - } - // Integ is optional - for _, transform := range proposal.IntegrityAlgorithm { - integKType := integ.DecodeTransformChildSA(transform) - if integKType != nil { - if childsa.integKInfo == nil { - childsa.integKInfo = integKType - } else { - if integKType.Priority() > childsa.integKInfo.Priority() { - childsa.integKInfo = integKType - } - } - } - } - for _, transform := range proposal.ExtendedSequenceNumbers { - esnType := esn.DecodeTransform(transform) - if esnType != nil { - if childsa.esnInfo == nil { - childsa.esnInfo = esnType - } else { - if esnType.Priority() > childsa.esnInfo.Priority() { - childsa.esnInfo = esnType - } - } - } - } - if childsa.esnInfo == nil { - return false // mandatory - } - if len(proposal.PseudorandomFunction) > 0 { - return false // No PRF - } - return true -} - -func (childsa *ChildSA) ToProposal() *message.Proposal { +func (childsaKey *ChildSAKey) ToProposal() (*message.Proposal, error) { p := new(message.Proposal) - p.ProtocolID = types.TypeESP - if childsa.dhInfo != nil { - p.DiffieHellmanGroup = append(p.DiffieHellmanGroup, dh.ToTransform(childsa.dhInfo)) - } - p.EncryptionAlgorithm = append(p.EncryptionAlgorithm, encr.ToTransformChildSA(childsa.encrKInfo)) - if childsa.integKInfo != nil { - p.IntegrityAlgorithm = append(p.IntegrityAlgorithm, integ.ToTransformChildSA(childsa.integKInfo)) - } - p.ExtendedSequenceNumbers = append(p.ExtendedSequenceNumbers, esn.ToTransform(childsa.esnInfo)) - return p -} - -func (childsa *ChildSA) SetProposal(proposal *message.Proposal) bool { - if len(proposal.DiffieHellmanGroup) == 1 { - if childsa.dhInfo = dh.DecodeTransform(proposal.DiffieHellmanGroup[0]); childsa.dhInfo == nil { - return false - } - } - if childsa.encrKInfo = encr.DecodeTransformChildSA(proposal.EncryptionAlgorithm[0]); childsa.encrKInfo == nil { - return false + p.ProtocolID = message.TypeESP + if childsaKey.DhInfo != nil { + p.DiffieHellmanGroup = append(p.DiffieHellmanGroup, dh.ToTransform(childsaKey.DhInfo)) } - if len(proposal.IntegrityAlgorithm) == 1 { - if childsa.integKInfo = integ.DecodeTransformChildSA(proposal.IntegrityAlgorithm[0]); childsa.encrKInfo == nil { - return false - } + encrKTransform, err := encr.ToTransformChildSA(childsaKey.EncrKInfo) + if err != nil { + return nil, errors.Wrapf(err, "ChildSAKey ToProposal") } - if childsa.esnInfo = esn.DecodeTransform(proposal.ExtendedSequenceNumbers[0]); childsa.esnInfo == nil { - return false + p.EncryptionAlgorithm = append(p.EncryptionAlgorithm, encrKTransform) + if childsaKey.IntegKInfo != nil { + p.IntegrityAlgorithm = append(p.IntegrityAlgorithm, integ.ToTransformChildSA(childsaKey.IntegKInfo)) } - return true + p.ExtendedSequenceNumbers = append(p.ExtendedSequenceNumbers, esn.ToTransform(childsaKey.EsnInfo)) + return p, nil } -// CalcKEMaterial generates secret and calculate Diffie-Hellman public key -// exchange material. -// Peer public value as parameter, return local public value and shared key. -func (childsa *ChildSA) CalcKEMaterial(peerPublicValue []byte) ([]byte, []byte) { - secret := GenerateRandomNumber() - peerPublicValueBig := new(big.Int).SetBytes(peerPublicValue) - return childsa.dhInfo.GetPublicValue(secret), childsa.dhInfo.GetSharedKey(secret, peerPublicValueBig) -} - -// Key Gen for child SA -func (childsa *ChildSA) GenerateKey(prf hash.Hash, dhSharedKey, concatenatedNonce []byte) error { - // Check parameters - if childsa == nil { - return errors.New("Child SA is nil") - } - - // Check if the context contain needed data - if prf == nil { - return errors.New("No pseudorandom function specified") - } - if childsa.encrKInfo == nil { - return errors.New("No encryption algorithm specified") - } - if childsa.esnInfo == nil { - return errors.New("No ESN present specified") - } - - // Get key length for encryption and integrity key for IPSec - var lengthEncrKeyIPSec, lengthIntegKeyIPSec, totalKeyLength int - - lengthEncrKeyIPSec = childsa.encrKInfo.GetKeyLength() - if childsa.integKInfo != nil { - lengthIntegKeyIPSec = childsa.integKInfo.GetKeyLength() +func NewChildSAKeyByProposal(proposal *message.Proposal) (*ChildSAKey, error) { + if proposal == nil { + return nil, errors.Errorf("NewChildSAKeyByProposal : proposal is nil") } - totalKeyLength = (lengthEncrKeyIPSec + lengthIntegKeyIPSec) * 2 - // Generate key for child security association as specified in RFC 7296 section 2.17 - var seed []byte - if childsa.dhInfo != nil && dhSharedKey != nil { - seed = append(dhSharedKey, concatenatedNonce...) - } else { - seed = concatenatedNonce + if len(proposal.EncryptionAlgorithm) == 0 { + return nil, errors.Errorf("NewChildSAKeyByProposal : EncryptionAlgorithm is nil") } - keyStream := lib.PrfPlus(prf, seed, totalKeyLength) - if keyStream == nil { - return errors.New("Error happened in PrfPlus") + if len(proposal.IntegrityAlgorithm) == 0 { + return nil, errors.Errorf("NewChildSAKeyByProposal : IntegrityAlgorithm is nil") } - childsa.initiatorToResponderEncrKey = append( - childsa.initiatorToResponderEncrKey, - keyStream[:lengthEncrKeyIPSec]...) - keyStream = keyStream[lengthEncrKeyIPSec:] - if childsa.integKInfo != nil { - childsa.initiatorToResponderIntegKey = append( - childsa.initiatorToResponderIntegKey, - keyStream[:lengthIntegKeyIPSec]...) - keyStream = keyStream[lengthIntegKeyIPSec:] + if len(proposal.ExtendedSequenceNumbers) == 0 { + return nil, errors.Errorf("NewChildSAKeyByProposal : ExtendedSequenceNumbers is nil") } - childsa.responderToInitiatorEncrKey = append( - childsa.responderToInitiatorEncrKey, - keyStream[:lengthEncrKeyIPSec]...) - keyStream = keyStream[lengthEncrKeyIPSec:] - if childsa.integKInfo != nil { - childsa.responderToInitiatorIntegKey = append( - childsa.responderToInitiatorIntegKey, - keyStream[:lengthIntegKeyIPSec]...) - } - - return nil -} -func (childsa *ChildSA) GenerateXFRMContext(role int) { - // Mark - mark := &netlink.XfrmMark{ - Value: childsa.Mark, - } - - // Initiator to responder state and policy - // State - s := new(netlink.XfrmState) - if role == types.Role_Initiator { - s.Src = childsa.LocalPublicIPAddr - s.Dst = childsa.RemotePublicIPAddr - } else { - s.Src = childsa.RemotePublicIPAddr - s.Dst = childsa.LocalPublicIPAddr - } - s.Proto = netlink.XFRM_PROTO_ESP - s.Mode = netlink.XFRM_MODE_TUNNEL - s.Spi = int(childsa.SPI) - s.Mark = mark - if childsa.integKInfo != nil { - s.Auth = &netlink.XfrmStateAlgo{ - Name: childsa.integKInfo.XFRMString(), - Key: childsa.initiatorToResponderIntegKey, - } - } - s.Crypt = &netlink.XfrmStateAlgo{ - Name: childsa.encrKInfo.XFRMString(), - Key: childsa.initiatorToResponderEncrKey, - } - s.ESN = childsa.esnInfo.Init() - if childsa.EnableEncap { - if role == types.Role_Initiator { - s.Encap = &netlink.XfrmStateEncap{ - Type: netlink.XFRM_ENCAP_ESPINUDP, - SrcPort: childsa.LocalPort, - DstPort: childsa.RemotePort, - } - } else { - s.Encap = &netlink.XfrmStateEncap{ - Type: netlink.XFRM_ENCAP_ESPINUDP, - SrcPort: childsa.RemotePort, - DstPort: childsa.LocalPort, - } + childsaKey := new(ChildSAKey) + if len(proposal.DiffieHellmanGroup) == 1 { + childsaKey.DhInfo = dh.DecodeTransform(proposal.DiffieHellmanGroup[0]) + if childsaKey.DhInfo == nil { + return nil, errors.Errorf("NewChildSAKeyByProposal : Get unsupport DiffieHellmanGroup[%v]", + proposal.DiffieHellmanGroup[0].TransformID) } } - // Policy - p := new(netlink.XfrmPolicy) - if role == types.Role_Initiator { - p.Src = childsa.TSLocal - p.Dst = childsa.TSRemote - p.Dir = netlink.XFRM_DIR_OUT - } else { - p.Src = childsa.TSRemote - p.Dst = childsa.TSLocal - p.Dir = netlink.XFRM_DIR_IN - } - p.Proto = netlink.Proto(childsa.IPProto) - p.Mark = mark - p.Tmpls = []netlink.XfrmPolicyTmpl{ - { - Src: s.Src, - Dst: s.Dst, - Proto: s.Proto, - Mode: s.Mode, - Spi: s.Spi, - }, + childsaKey.EncrKInfo = encr.DecodeTransformChildSA(proposal.EncryptionAlgorithm[0]) + if childsaKey.EncrKInfo == nil { + return nil, errors.Errorf("NewChildSAKeyByProposal : Get unsupport EncryptionAlgorithm[%v]", + proposal.EncryptionAlgorithm[0].TransformID) } - childsa.initiatorToResponderState = s - childsa.initiatorToResponderPolicy = p - - // Responder to initiator state and policy - // State - s = new(netlink.XfrmState) - if role == types.Role_Initiator { - s.Src = childsa.RemotePublicIPAddr - s.Dst = childsa.LocalPublicIPAddr - } else { - s.Src = childsa.LocalPublicIPAddr - s.Dst = childsa.RemotePublicIPAddr - } - s.Proto = netlink.XFRM_PROTO_ESP - s.Mode = netlink.XFRM_MODE_TUNNEL - s.Spi = int(childsa.SPI) - s.Mark = mark - if childsa.integKInfo != nil { - s.Auth = &netlink.XfrmStateAlgo{ - Name: childsa.integKInfo.XFRMString(), - Key: childsa.responderToInitiatorIntegKey, - } - } - s.Crypt = &netlink.XfrmStateAlgo{ - Name: childsa.encrKInfo.XFRMString(), - Key: childsa.responderToInitiatorEncrKey, - } - s.ESN = childsa.esnInfo.Init() - if childsa.EnableEncap { - if role == types.Role_Initiator { - s.Encap = &netlink.XfrmStateEncap{ - Type: netlink.XFRM_ENCAP_ESPINUDP, - SrcPort: childsa.RemotePort, - DstPort: childsa.LocalPort, - } - } else { - s.Encap = &netlink.XfrmStateEncap{ - Type: netlink.XFRM_ENCAP_ESPINUDP, - SrcPort: childsa.LocalPort, - DstPort: childsa.RemotePort, - } + if len(proposal.IntegrityAlgorithm) == 1 { + childsaKey.IntegKInfo = integ.DecodeTransformChildSA(proposal.IntegrityAlgorithm[0]) + if childsaKey.IntegKInfo == nil { + return nil, errors.Errorf("NewChildSAKeyByProposal : Get unsupport IntegrityAlgorithm[%v]", + proposal.IntegrityAlgorithm[0].TransformID) } } - // Policy - p = new(netlink.XfrmPolicy) - if role == types.Role_Initiator { - p.Src = childsa.TSRemote - p.Dst = childsa.TSLocal - p.Dir = netlink.XFRM_DIR_IN - } else { - p.Src = childsa.TSLocal - p.Dst = childsa.TSRemote - p.Dir = netlink.XFRM_DIR_OUT - } - p.Proto = netlink.Proto(childsa.IPProto) - p.Mark = mark - p.Tmpls = []netlink.XfrmPolicyTmpl{ - { - Src: s.Src, - Dst: s.Dst, - Proto: s.Proto, - Mode: s.Mode, - Spi: s.Spi, - }, - } - - childsa.responderToInitiatorState = s - childsa.responderToInitiatorPolicy = p -} - -func (childsa *ChildSA) XFRMRuleAdd() error { - if err := netlink.XfrmStateAdd(childsa.initiatorToResponderState); err != nil { - secLog.Errorf("Add XFRM state failed: %+v", err) - return errors.New("Add XFRM initiator to responder state failed") - } - if err := netlink.XfrmPolicyAdd(childsa.initiatorToResponderPolicy); err != nil { - secLog.Errorf("Add XFRM policy failed: %+v", err) - return errors.New("Add XFRM initiator to responder policy failed") - } - if err := netlink.XfrmStateAdd(childsa.responderToInitiatorState); err != nil { - secLog.Errorf("Add XFRM state failed: %+v", err) - return errors.New("Add XFRM responder to initiator state failed") - } - if err := netlink.XfrmPolicyAdd(childsa.responderToInitiatorPolicy); err != nil { - secLog.Errorf("Add XFRM policy failed: %+v", err) - return errors.New("Add XFRM responder to initiator policy failed") - } - return nil -} - -func (childsa *ChildSA) XFRMRuleFlush() error { - if err := netlink.XfrmStateDel(childsa.initiatorToResponderState); err != nil { - secLog.Errorf("Delete XFRM state failed: %+v", err) - return errors.New("Delete XFRM initiator to responder state failed") - } - if err := netlink.XfrmPolicyDel(childsa.initiatorToResponderPolicy); err != nil { - secLog.Errorf("Delete XFRM policy failed: %+v", err) - return errors.New("Delete XFRM initiator to responder policy failed") - } - if err := netlink.XfrmStateDel(childsa.responderToInitiatorState); err != nil { - secLog.Errorf("Delete XFRM state failed: %+v", err) - return errors.New("Delete XFRM responder to initiator state failed") - } - if err := netlink.XfrmPolicyDel(childsa.responderToInitiatorPolicy); err != nil { - secLog.Errorf("Delete XFRM policy failed: %+v", err) - return errors.New("Delete XFRM responder to initiator policy failed") - } - return nil -} - -/* Archive for future use -// Certificate -func CompareRootCertificate(certificateEncoding uint8, requestedCertificateAuthorityHash []byte) bool { - if certificateEncoding != types.X509CertificateSignature { - secLog.Debugf("Not support certificate type: %d. Reject.", certificateEncoding) - return false - } - - n3iwfSelf := context.N3IWFSelf() - - if len(n3iwfSelf.CertificateAuthority) == 0 { - secLog.Error("Certificate authority in context is empty") - return false - } - - return bytes.Equal(n3iwfSelf.CertificateAuthority, requestedCertificateAuthorityHash) -} -*/ - -/* -func VerifyIKEChecksum(key []byte, originData []byte, checksum []byte, algorithmType uint16) (bool, error) { - switch algorithmType { - case message.AUTH_HMAC_MD5_96: - if len(key) != 16 { - return false, errors.New("Unmatched input key length") - } - integrityFunction := hmac.New(md5.New, key) - if _, err := integrityFunction.Write(originData); err != nil { - secLog.Errorf("Hash function write error when verifying IKE checksum: %+v", err) - return false, errors.New("Hash function write error") - } - checksumOfMessage := integrityFunction.Sum(nil) - - secLog.Tracef("Calculated checksum:\n%s\nReceived checksum:\n%s", - hex.Dump(checksumOfMessage), hex.Dump(checksum)) - - return hmac.Equal(checksumOfMessage, checksum), nil - case message.AUTH_HMAC_SHA1_96: - if len(key) != 20 { - return false, errors.New("Unmatched input key length") - } - integrityFunction := hmac.New(sha1.New, key) - if _, err := integrityFunction.Write(originData); err != nil { - secLog.Errorf("Hash function write error when verifying IKE checksum: %+v", err) - return false, errors.New("Hash function write error") - } - checksumOfMessage := integrityFunction.Sum(nil)[:12] - - secLog.Tracef("Calculated checksum:\n%s\nReceived checksum:\n%s", - hex.Dump(checksumOfMessage), hex.Dump(checksum)) - - return hmac.Equal(checksumOfMessage, checksum), nil - default: - secLog.Errorf("Unsupported integrity function: %d", algorithmType) - return false, errors.New("Unsupported algorithm") - } -} - -// Decrypt -func DecryptProcedure(ikeSecurityAssociation *context.IKESecurityAssociation, ikeMessage *message.IKEMessage, - encryptedPayload *message.Encrypted) (message.IKEPayloadContainer, error) { - // Check parameters - if ikeSecurityAssociation == nil { - return nil, errors.New("IKE SA is nil") - } - if ikeMessage == nil { - return nil, errors.New("IKE message is nil") - } - if encryptedPayload == nil { - return nil, errors.New("IKE encrypted payload is nil") - } - - // Check if the context contain needed data - if ikeSecurityAssociation.IntegrityAlgorithm == nil { - return nil, errors.New("No integrity algorithm specified") - } - if ikeSecurityAssociation.EncryptionAlgorithm == nil { - return nil, errors.New("No encryption algorithm specified") - } - - if len(ikeSecurityAssociation.SK_ai) == 0 { - return nil, errors.New("No initiator's integrity key") - } - if len(ikeSecurityAssociation.SK_ei) == 0 { - return nil, errors.New("No initiator's encryption key") - } - - // Load needed information - transformIntegrityAlgorithm := ikeSecurityAssociation.IntegrityAlgorithm - transformEncryptionAlgorithm := ikeSecurityAssociation.EncryptionAlgorithm - checksumLength, ok := getOutputLength(transformIntegrityAlgorithm.TransformType, - transformIntegrityAlgorithm.TransformID, transformIntegrityAlgorithm.AttributePresent, - transformIntegrityAlgorithm.AttributeValue) - if !ok { - secLog.Error("Get key length of an unsupported algorithm. This may imply an unsupported transform is chosen.") - return nil, errors.New("Get key length failed") - } - - // Checksum - checksum := encryptedPayload.EncryptedData[len(encryptedPayload.EncryptedData)-checksumLength:] - - ikeMessageData, err := ikeMessage.Encode() - if err != nil { - secLog.Errorln(err) - secLog.Error("Error occur when encoding for checksum") - return nil, errors.New("Encoding IKE message failed") - } - - ok, err = VerifyIKEChecksum(ikeSecurityAssociation.SK_ai, - ikeMessageData[:len(ikeMessageData)-checksumLength], checksum, - transformIntegrityAlgorithm.TransformID) - if err != nil { - secLog.Errorf("Error occur when verifying checksum: %+v", err) - return nil, errors.New("Error verify checksum") - } - if !ok { - secLog.Warn("Message checksum failed. Drop the message.") - return nil, errors.New("Checksum failed, drop.") - } - - // Decrypt - encryptedData := encryptedPayload.EncryptedData[:len(encryptedPayload.EncryptedData)-checksumLength] - plainText, err := DecryptMessage(ikeSecurityAssociation.SK_ei, encryptedData, - transformEncryptionAlgorithm.TransformID) - if err != nil { - secLog.Errorf("Error occur when decrypting message: %+v", err) - return nil, errors.New("Error decrypting message") - } - - var decryptedIKEPayload message.IKEPayloadContainer - err = decryptedIKEPayload.Decode(encryptedPayload.NextPayload, plainText) + var err error + childsaKey.EsnInfo, err = esn.DecodeTransform(proposal.ExtendedSequenceNumbers[0]) if err != nil { - secLog.Errorln(err) - return nil, errors.New("Decoding decrypted payload failed") + return nil, errors.Wrapf(err, "NewChildSAKeyByProposal") } - return decryptedIKEPayload, nil - + return childsaKey, nil } -// Encrypt -func EncryptProcedure(ikeSecurityAssociation *context.IKESecurityAssociation, - ikePayload message.IKEPayloadContainer, responseIKEMessage *message.IKEMessage) error { +// Key Gen for child SA +func (childsaKey *ChildSAKey) GenerateKeyForChildSA( + ikeSA *IKESAKey, + concatenatedNonce []byte, +) error { // Check parameters - if ikeSecurityAssociation == nil { - return errors.New("IKE SA is nil") - } - if len(ikePayload) == 0 { - return errors.New("No IKE payload to be encrypted") + if ikeSA == nil { + return errors.Errorf("IKE SA is nil") } - if responseIKEMessage == nil { - return errors.New("Response IKE message is nil") + if childsaKey == nil { + return errors.Errorf("Child SA is nil") } // Check if the context contain needed data - if ikeSecurityAssociation.IntegrityAlgorithm == nil { - return errors.New("No integrity algorithm specified") + if ikeSA.PrfInfo == nil { + return errors.Errorf("No pseudorandom function specified") } - if ikeSecurityAssociation.EncryptionAlgorithm == nil { - return errors.New("No encryption algorithm specified") - } - - if len(ikeSecurityAssociation.SK_ar) == 0 { - return errors.New("No responder's integrity key") + if childsaKey.EncrKInfo == nil { + return errors.Errorf("No encryption algorithm specified") } - if len(ikeSecurityAssociation.SK_er) == 0 { - return errors.New("No responder's encryption key") + if ikeSA.Prf_d == nil { + return errors.Errorf("No key deriving key") } - // Load needed information - transformIntegrityAlgorithm := ikeSecurityAssociation.IntegrityAlgorithm - transformEncryptionAlgorithm := ikeSecurityAssociation.EncryptionAlgorithm - checksumLength, ok := getOutputLength(transformIntegrityAlgorithm.TransformType, - transformIntegrityAlgorithm.TransformID, transformIntegrityAlgorithm.AttributePresent, - transformIntegrityAlgorithm.AttributeValue) - if !ok { - secLog.Error("Get key length of an unsupported algorithm. This may imply an unsupported transform is chosen.") - return errors.New("Get key length failed") - } - - // Encrypting - ikePayloadData, err := ikePayload.Encode() - if err != nil { - secLog.Error(err) - return errors.New("Encoding IKE payload failed.") - } + // Get key length for encryption and integrity key for IPSec + var lengthEncryptionKeyIPSec, lengthIntegrityKeyIPSec, totalKeyLength int - encryptedData, err := EncryptMessage(ikeSecurityAssociation.SK_er, ikePayloadData, - transformEncryptionAlgorithm.TransformID) - if err != nil { - secLog.Errorf("Encrypting data error: %+v", err) - return errors.New("Error encrypting message") + lengthEncryptionKeyIPSec = childsaKey.EncrKInfo.GetKeyLength() + if childsaKey.IntegKInfo != nil { + lengthIntegrityKeyIPSec = childsaKey.IntegKInfo.GetKeyLength() } + totalKeyLength = (lengthEncryptionKeyIPSec + lengthIntegrityKeyIPSec) * 2 - encryptedData = append(encryptedData, make([]byte, checksumLength)...) - sk := responseIKEMessage.Payloads.BuildEncrypted(ikePayload[0].Type(), encryptedData) + // Generate key for child security association as specified in RFC 7296 section 2.17 + seed := concatenatedNonce - // Calculate checksum - responseIKEMessageData, err := responseIKEMessage.Encode() - if err != nil { - secLog.Error(err) - return errors.New("Encoding IKE message error") - } - checksumOfMessage, err := CalculateChecksum(ikeSecurityAssociation.SK_ar, - responseIKEMessageData[:len(responseIKEMessageData)-checksumLength], - transformIntegrityAlgorithm.TransformID) - if err != nil { - secLog.Errorf("Calculating checksum failed: %+v", err) - return errors.New("Error calculating checksum") - } - checksumField := sk.EncryptedData[len(sk.EncryptedData)-checksumLength:] - copy(checksumField, checksumOfMessage) + keyStream := lib.PrfPlus(ikeSA.Prf_d, seed, totalKeyLength) + if keyStream == nil { + return errors.Errorf("Error happened in PrfPlus") + } + + childsaKey.InitiatorToResponderEncryptionKey = append( + childsaKey.InitiatorToResponderEncryptionKey, + keyStream[:lengthEncryptionKeyIPSec]...) + keyStream = keyStream[lengthEncryptionKeyIPSec:] + childsaKey.InitiatorToResponderIntegrityKey = append( + childsaKey.InitiatorToResponderIntegrityKey, + keyStream[:lengthIntegrityKeyIPSec]...) + keyStream = keyStream[lengthIntegrityKeyIPSec:] + childsaKey.ResponderToInitiatorEncryptionKey = append( + childsaKey.ResponderToInitiatorEncryptionKey, + keyStream[:lengthEncryptionKeyIPSec]...) + keyStream = keyStream[lengthEncryptionKeyIPSec:] + childsaKey.ResponderToInitiatorIntegrityKey = append( + childsaKey.ResponderToInitiatorIntegrityKey, + keyStream[:lengthIntegrityKeyIPSec]...) return nil - } -// Get information of algorithm -func getKeyLength(transformType uint8, transformID uint16, attributePresent bool, - attributeValue uint16) (int, bool) { - switch transformType { - case message.TypeEncryptionAlgorithm: - switch transformID { - case message.ENCR_DES_IV64: - return 0, false - case message.ENCR_DES: - return 8, true - case message.ENCR_3DES: - return 24, true - case message.ENCR_RC5: - return 0, false - case message.ENCR_IDEA: - return 0, false - case message.ENCR_CAST: - if attributePresent { - switch attributeValue { - case 128: - return 16, true - case 256: - return 0, false - default: - return 0, false - } - } - return 0, false - case message.ENCR_BLOWFISH: // Blowfish support variable key length - if attributePresent { - if attributeValue < 40 { - return 0, false - } else if attributeValue > 448 { - return 0, false - } else { - return int(attributeValue / 8), true - } - } else { - return 0, false - } - case message.ENCR_3IDEA: - return 0, false - case message.ENCR_DES_IV32: - return 0, false - case message.ENCR_NULL: - return 0, true - case message.ENCR_AES_CBC: - if attributePresent { - switch attributeValue { - case 128: - return 16, true - case 192: - return 24, true - case 256: - return 32, true - default: - return 0, false - } - } else { - return 0, false - } - case message.ENCR_AES_CTR: - if attributePresent { - switch attributeValue { - case 128: - return 20, true - case 192: - return 28, true - case 256: - return 36, true - default: - return 0, false - } - } else { - return 0, false - } - default: - return 0, false - } - case message.TypePseudorandomFunction: - switch transformID { - case message.PRF_HMAC_MD5: - return 16, true - case message.PRF_HMAC_SHA1: - return 20, true - case message.PRF_HMAC_TIGER: - return 0, false - default: - return 0, false - } - case message.TypeIntegrityAlgorithm: - switch transformID { - case message.AUTH_NONE: - return 0, false - case message.AUTH_HMAC_MD5_96: - return 16, true - case message.AUTH_HMAC_SHA1_96: - return 20, true - case message.AUTH_DES_MAC: - return 0, false - case message.AUTH_KPDK_MD5: - return 0, false - case message.AUTH_AES_XCBC_96: - return 0, false - default: - return 0, false - } - case message.TypeDiffieHellmanGroup: - switch transformID { - case message.DH_NONE: - return 0, false - case message.DH_768_BIT_MODP: - return 0, false - case message.DH_1024_BIT_MODP: - return 0, false - case message.DH_1536_BIT_MODP: - return 0, false - case message.DH_2048_BIT_MODP: - return 0, false - case message.DH_3072_BIT_MODP: - return 0, false - case message.DH_4096_BIT_MODP: - return 0, false - case message.DH_6144_BIT_MODP: - return 0, false - case message.DH_8192_BIT_MODP: - return 0, false - default: - return 0, false - } - default: - return 0, false +// Certificate +func CompareRootCertificate( + ca []byte, + certificateEncoding uint8, + requestedCertificateAuthorityHash []byte, +) bool { + if certificateEncoding != message.X509CertificateSignature { + // fmt.Printf("Not support certificate type: %d. Reject.", certificateEncoding) + return false } -} -func getOutputLength(transformType uint8, transformID uint16, attributePresent bool, - attributeValue uint16) (int, bool) { - switch transformType { - case message.TypePseudorandomFunction: - switch transformID { - case message.PRF_HMAC_MD5: - return 16, true - case message.PRF_HMAC_SHA1: - return 20, true - case message.PRF_HMAC_TIGER: - return 0, false - default: - return 0, false - } - case message.TypeIntegrityAlgorithm: - switch transformID { - case message.AUTH_NONE: - return 0, false - case message.AUTH_HMAC_MD5_96: - return 12, true - case message.AUTH_HMAC_SHA1_96: - return 12, true - case message.AUTH_DES_MAC: - return 0, false - case message.AUTH_KPDK_MD5: - return 0, false - case message.AUTH_AES_XCBC_96: - return 0, false - default: - return 0, false - } - default: - return 0, false + if len(ca) == 0 { + // fmt.Printf("Certificate authority in context is empty") + return false } + + return bytes.Equal(ca, requestedCertificateAuthorityHash) } -*/ diff --git a/security/security_test.go b/security/security_test.go index 5b55b07..55ecef2 100644 --- a/security/security_test.go +++ b/security/security_test.go @@ -2,17 +2,19 @@ package security import ( "bytes" + "encoding/hex" "fmt" "sync" "testing" - "github.com/free5gc/ike/internal/dh" - "github.com/free5gc/ike/internal/encr" - "github.com/free5gc/ike/internal/esn" - "github.com/free5gc/ike/internal/integ" - "github.com/free5gc/ike/internal/prf" + "github.com/stretchr/testify/require" + "github.com/free5gc/ike/message" - "github.com/free5gc/ike/types" + "github.com/free5gc/ike/security/dh" + "github.com/free5gc/ike/security/encr" + "github.com/free5gc/ike/security/esn" + "github.com/free5gc/ike/security/integ" + "github.com/free5gc/ike/security/prf" ) func TestGenerateRandomNumber(t *testing.T) { @@ -22,7 +24,8 @@ func TestGenerateRandomNumber(t *testing.T) { for i := 0; i < 100; i++ { wg.Add(1) go func(wg *sync.WaitGroup) { - num := GenerateRandomNumber() + num, err := GenerateRandomNumber() + require.NoError(t, err) if num == nil { fmt.Print("Generate random number failed.") } else { @@ -64,210 +67,21 @@ func TestConcatenateNonceAndSPI(t *testing.T) { } } -func TestIKESelectProposal(t *testing.T) { - // Types' pointers - dhType1 := dh.StrToType("DH_1024_BIT_MODP") - dhType2 := dh.StrToType("DH_2048_BIT_MODP") - // encrType1 := encr.StrToType("ENCR_AES_CBC_128") - // encrType2 := encr.StrToType("ENCR_AES_CBC_192") - encrType3 := encr.StrToType("ENCR_AES_CBC_256") - // integType1 := integ.StrToType("AUTH_HMAC_MD5_96") - integType2 := integ.StrToType("AUTH_HMAC_SHA1_96") - // prfType1 := prf.StrToType("PRF_HMAC_MD5") - prfType2 := prf.StrToType("PRF_HMAC_SHA1") - - // Transforms - t1 := &message.Transform{ - TransformType: types.TypeDiffieHellmanGroup, - TransformID: types.DH_1024_BIT_MODP, - AttributePresent: false, - } - t2 := &message.Transform{ - TransformType: types.TypeDiffieHellmanGroup, - TransformID: types.DH_2048_BIT_MODP, - AttributePresent: false, - } - t3 := &message.Transform{ - TransformType: types.TypeDiffieHellmanGroup, - TransformID: types.DH_1536_BIT_MODP, - AttributePresent: false, - } - t4 := &message.Transform{ - TransformType: types.TypeEncryptionAlgorithm, - TransformID: types.ENCR_AES_CBC, - AttributePresent: true, - AttributeFormat: types.AttributeFormatUseTV, - AttributeType: types.AttributeTypeKeyLength, - AttributeValue: 128, - } - t5 := &message.Transform{ - TransformType: types.TypeEncryptionAlgorithm, - TransformID: types.ENCR_AES_CBC, - AttributePresent: true, - AttributeFormat: types.AttributeFormatUseTV, - AttributeType: types.AttributeTypeKeyLength, - AttributeValue: 192, - } - t6 := &message.Transform{ - TransformType: types.TypeEncryptionAlgorithm, - TransformID: types.ENCR_AES_CBC, - AttributePresent: true, - AttributeFormat: types.AttributeFormatUseTV, - AttributeType: types.AttributeTypeKeyLength, - AttributeValue: 256, - } - t7 := &message.Transform{ - TransformType: types.TypeEncryptionAlgorithm, - TransformID: types.ENCR_AES_CBC, - AttributePresent: true, - AttributeFormat: types.AttributeFormatUseTV, - AttributeType: types.AttributeTypeKeyLength, - AttributeValue: 384, - } - t8 := &message.Transform{ - TransformType: types.TypeEncryptionAlgorithm, - TransformID: types.ENCR_3DES, - AttributePresent: true, - AttributeFormat: types.AttributeFormatUseTV, - AttributeType: types.AttributeTypeKeyLength, - AttributeValue: 128, - } - t9 := &message.Transform{ - TransformType: types.TypeIntegrityAlgorithm, - TransformID: types.AUTH_HMAC_MD5_96, - AttributePresent: false, - } - t10 := &message.Transform{ - TransformType: types.TypeIntegrityAlgorithm, - TransformID: types.AUTH_HMAC_SHA1_96, - AttributePresent: false, - } - t11 := &message.Transform{ - TransformType: types.TypeIntegrityAlgorithm, - TransformID: types.AUTH_DES_MAC, - AttributePresent: false, - } - t12 := &message.Transform{ - TransformType: types.TypePseudorandomFunction, - TransformID: types.PRF_HMAC_MD5, - AttributePresent: false, - } - t13 := &message.Transform{ - TransformType: types.TypePseudorandomFunction, - TransformID: types.PRF_HMAC_SHA1, - AttributePresent: false, - } - t14 := &message.Transform{ - TransformType: types.TypePseudorandomFunction, - TransformID: types.PRF_HMAC_TIGER, - AttributePresent: false, - } - t15 := &message.Transform{ - TransformType: types.TypeExtendedSequenceNumbers, - TransformID: types.ESN_ENABLE, - AttributePresent: false, - } - - // Proposal 1 - proposal := new(message.Proposal) - proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, t3) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t7) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t8) - proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, t9) - proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, t13) - - ikesa := new(IKESA) - if ikesa.SelectProposal(proposal) { - t.Fatal("SelectProposal returned a false result") - } - - // Proposal 2 - proposal = new(message.Proposal) - proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, t1) - proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, t2) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t4) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t5) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t6) - proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, t10) - proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, t11) - proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, t12) - proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, t13) - proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, t14) - - ikesa = new(IKESA) - if !ikesa.SelectProposal(proposal) { - t.Fatal("SelectProposal returned a false result") - } - - if ikesa.dhInfo != dhType2 || ikesa.encrInfo != encrType3 || - ikesa.integInfo != integType2 || ikesa.prfInfo != prfType2 { - t.Fatal("SelectProposal selected a false result") - } - - newPriority := map[string]uint32{ - "DH_1024_BIT_MODP": 1, - "DH_2048_BIT_MODP": 0, - } - if err := dh.SetPriority(newPriority); err != nil { - t.Fatalf("Set priority failed: %v", err) - } - - ikesa = new(IKESA) - if !ikesa.SelectProposal(proposal) { - t.Fatal("SelectProposal returned a false result") - } - - if ikesa.dhInfo != dhType1 || ikesa.encrInfo != encrType3 || - ikesa.integInfo != integType2 || ikesa.prfInfo != prfType2 { - t.Fatal("SelectProposal selected a false result") - } - - // reset priority - newPriority = map[string]uint32{ - "DH_1024_BIT_MODP": 0, - "DH_2048_BIT_MODP": 1, - } - if err := dh.SetPriority(newPriority); err != nil { - t.Fatalf("Set priority failed: %v", err) - } - - // Proposal 3 - proposal = new(message.Proposal) - - ikesa = new(IKESA) - if ikesa.SelectProposal(proposal) { - t.Fatal("SelectProposal returned a false result") - } - - // Proposal 4 - proposal = new(message.Proposal) - proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, t2) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t5) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t6) - proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, t9) - proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, t13) - proposal.ExtendedSequenceNumbers = append(proposal.ExtendedSequenceNumbers, t15) - - ikesa = new(IKESA) - if ikesa.SelectProposal(proposal) { - t.Fatal("SelectProposal returned a false result") - } -} - func TestIKEToProposal(t *testing.T) { dhType := dh.StrToType("DH_1024_BIT_MODP") encrType := encr.StrToType("ENCR_AES_CBC_256") integType := integ.StrToType("AUTH_HMAC_MD5_96") prfType := prf.StrToType("PRF_HMAC_SHA1") - ikesa := IKESA{ - dhInfo: dhType, - encrInfo: encrType, - integInfo: integType, - prfInfo: prfType, + ikesaKey := IKESAKey{ + DhInfo: dhType, + EncrInfo: encrType, + IntegInfo: integType, + PrfInfo: prfType, } - proposal := ikesa.ToProposal() + proposal, err := ikesaKey.ToProposal() + require.NoError(t, err) if len(proposal.DiffieHellmanGroup) != 1 || len(proposal.EncryptionAlgorithm) != 1 || @@ -287,251 +101,196 @@ func TestIKESetProposal(t *testing.T) { proposal := new(message.Proposal) proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, dh.ToTransform(dhType)) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, encr.ToTransform(encrType)) + encrTranform, err := encr.ToTransform(encrType) + require.NoError(t, err) + proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, encrTranform) proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, integ.ToTransform(integType)) proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, prf.ToTransform(prfType)) - ikesa := new(IKESA) + concatenatedNonce := []byte{0x01, 0x02, 0x03, 0x04} + keyexChange := []byte{0x05, 0x06, 0x07, 0x08} - ikesa.SetProposal(proposal) + ikesaKey, _, err := NewIKESAKey(proposal, keyexChange, concatenatedNonce, + 0x123, 0x456) + require.NoError(t, err) - if ikesa.dhInfo == nil || - ikesa.encrInfo == nil || - ikesa.integInfo == nil || - ikesa.prfInfo == nil { + if ikesaKey.DhInfo == nil || + ikesaKey.EncrInfo == nil || + ikesaKey.IntegInfo == nil || + ikesaKey.PrfInfo == nil { t.FailNow() } } -func TestChildSelectProposal(t *testing.T) { - // Types' pointers - dhType1 := dh.StrToType("DH_1024_BIT_MODP") - dhType2 := dh.StrToType("DH_2048_BIT_MODP") - // encrKType1 := encr.StrToKType("ENCR_AES_CBC_128") - // encrKType2 := encr.StrToKType("ENCR_AES_CBC_192") - encrKType3 := encr.StrToKType("ENCR_AES_CBC_256") - // integKType1 := integ.StrToKType("AUTH_HMAC_MD5_96") - integKType2 := integ.StrToKType("AUTH_HMAC_SHA1_96") - // prfType1 := prf.StrToType("PRF_HMAC_MD5") - // prfType2 := prf.StrToType("PRF_HMAC_SHA1") - // esnType1 := esn.StrToType("ESN_ENABLE") - esnType2 := esn.StrToType("ESN_DISABLE") - - // Transforms - t1 := &message.Transform{ - TransformType: types.TypeDiffieHellmanGroup, - TransformID: types.DH_1024_BIT_MODP, - AttributePresent: false, - } - t2 := &message.Transform{ - TransformType: types.TypeDiffieHellmanGroup, - TransformID: types.DH_2048_BIT_MODP, - AttributePresent: false, - } - t3 := &message.Transform{ - TransformType: types.TypeDiffieHellmanGroup, - TransformID: types.DH_1536_BIT_MODP, - AttributePresent: false, - } - t4 := &message.Transform{ - TransformType: types.TypeEncryptionAlgorithm, - TransformID: types.ENCR_AES_CBC, - AttributePresent: true, - AttributeFormat: types.AttributeFormatUseTV, - AttributeType: types.AttributeTypeKeyLength, - AttributeValue: 128, - } - t5 := &message.Transform{ - TransformType: types.TypeEncryptionAlgorithm, - TransformID: types.ENCR_AES_CBC, - AttributePresent: true, - AttributeFormat: types.AttributeFormatUseTV, - AttributeType: types.AttributeTypeKeyLength, - AttributeValue: 192, - } - t6 := &message.Transform{ - TransformType: types.TypeEncryptionAlgorithm, - TransformID: types.ENCR_AES_CBC, - AttributePresent: true, - AttributeFormat: types.AttributeFormatUseTV, - AttributeType: types.AttributeTypeKeyLength, - AttributeValue: 256, - } - t7 := &message.Transform{ - TransformType: types.TypeEncryptionAlgorithm, - TransformID: types.ENCR_AES_CBC, - AttributePresent: true, - AttributeFormat: types.AttributeFormatUseTV, - AttributeType: types.AttributeTypeKeyLength, - AttributeValue: 384, - } - t8 := &message.Transform{ - TransformType: types.TypeEncryptionAlgorithm, - TransformID: types.ENCR_3DES, - AttributePresent: true, - AttributeFormat: types.AttributeFormatUseTV, - AttributeType: types.AttributeTypeKeyLength, - AttributeValue: 128, - } - t9 := &message.Transform{ - TransformType: types.TypeIntegrityAlgorithm, - TransformID: types.AUTH_HMAC_MD5_96, - AttributePresent: false, - } - t10 := &message.Transform{ - TransformType: types.TypeIntegrityAlgorithm, - TransformID: types.AUTH_HMAC_SHA1_96, - AttributePresent: false, - } - t11 := &message.Transform{ - TransformType: types.TypeIntegrityAlgorithm, - TransformID: types.AUTH_DES_MAC, - AttributePresent: false, - } - t12 := &message.Transform{ - TransformType: types.TypePseudorandomFunction, - TransformID: types.PRF_HMAC_MD5, - AttributePresent: false, - } - t13 := &message.Transform{ - TransformType: types.TypePseudorandomFunction, - TransformID: types.PRF_HMAC_SHA1, - AttributePresent: false, - } - t14 := &message.Transform{ - TransformType: types.TypePseudorandomFunction, - TransformID: types.PRF_HMAC_TIGER, - AttributePresent: false, - } - t15 := &message.Transform{ - TransformType: types.TypeExtendedSequenceNumbers, - TransformID: types.ESN_ENABLE, - AttributePresent: false, - } - t16 := &message.Transform{ - TransformType: types.TypeExtendedSequenceNumbers, - TransformID: types.ESN_DISABLE, - AttributePresent: false, - } - - // Proposal 1 - proposal := new(message.Proposal) - proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, t3) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t7) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t8) - proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, t9) - proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, t13) - - childsa := new(ChildSA) - if childsa.SelectProposal(proposal) { - t.Fatal("SelectProposal returned a false result") - } - - // Proposal 2 - proposal = new(message.Proposal) - proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, t1) - proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, t2) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t4) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t5) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t6) - proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, t10) - proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, t11) - proposal.ExtendedSequenceNumbers = append(proposal.ExtendedSequenceNumbers, t15) - proposal.ExtendedSequenceNumbers = append(proposal.ExtendedSequenceNumbers, t16) - - childsa = new(ChildSA) - if !childsa.SelectProposal(proposal) { - t.Fatal("SelectProposal returned a false result") - } - - if childsa.dhInfo != dhType2 || childsa.encrKInfo != encrKType3 || - childsa.integKInfo != integKType2 || childsa.esnInfo != esnType2 { - t.Fatal("SelectProposal selected a false result") - } - - newPriority := map[string]uint32{ - "DH_1024_BIT_MODP": 1, - "DH_2048_BIT_MODP": 0, - } - if err := dh.SetPriority(newPriority); err != nil { - t.Fatalf("Set priority failed: %v", err) - } - - childsa = new(ChildSA) - if !childsa.SelectProposal(proposal) { - t.Fatal("SelectProposal returned a false result") - } - - if childsa.dhInfo != dhType1 || childsa.encrKInfo != encrKType3 || - childsa.integKInfo != integKType2 || childsa.esnInfo != esnType2 { - t.Fatal("SelectProposal selected a false result") - } - - // reset priority - newPriority = map[string]uint32{ - "DH_1024_BIT_MODP": 0, - "DH_2048_BIT_MODP": 1, - } - if err := dh.SetPriority(newPriority); err != nil { - t.Fatalf("Set priority failed: %v", err) - } - - // Proposal 3 - proposal = new(message.Proposal) - - childsa = new(ChildSA) - if childsa.SelectProposal(proposal) { - t.Fatal("SelectProposal returned a false result") - } - - // Proposal 4 - proposal = new(message.Proposal) - proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, t2) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t5) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t6) - proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, t9) - proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, t12) - proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, t13) - proposal.PseudorandomFunction = append(proposal.PseudorandomFunction, t14) - proposal.ExtendedSequenceNumbers = append(proposal.ExtendedSequenceNumbers, t15) - - childsa = new(ChildSA) - if childsa.SelectProposal(proposal) { - t.Fatal("SelectProposal returned a false result") - } - - // Proposal 5 - proposal = new(message.Proposal) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t5) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, t6) - proposal.ExtendedSequenceNumbers = append(proposal.ExtendedSequenceNumbers, t15) - proposal.ExtendedSequenceNumbers = append(proposal.ExtendedSequenceNumbers, t16) - - childsa = new(ChildSA) - if !childsa.SelectProposal(proposal) { - t.Fatal("SelectProposal returned a false result") - } +func TestGenerateKeyForIKESA(t *testing.T) { + concatenatedNonce := []byte{0x01, 0x02, 0x03, 0x04} + diffieHellmanSharedKey := []byte{0x05, 0x06, 0x07, 0x08} + initiatorSPI := uint64(0x456) + responderSPI := uint64(0x123) + + // IKE Security Association is nil + var ikesaKey *IKESAKey + err := ikesaKey.GenerateKeyForIKESA(concatenatedNonce, diffieHellmanSharedKey, + initiatorSPI, responderSPI) + require.Error(t, err) + + ikesaKey = &IKESAKey{} + + // Encryption algorithm is nil + err = ikesaKey.GenerateKeyForIKESA(concatenatedNonce, diffieHellmanSharedKey, + initiatorSPI, responderSPI) + require.Error(t, err) + + ikesaKey.EncrInfo = encr.StrToType("ENCR_AES_CBC_256") + + // Integrity algorithm is nil + err = ikesaKey.GenerateKeyForIKESA(concatenatedNonce, diffieHellmanSharedKey, + initiatorSPI, responderSPI) + require.Error(t, err) + + ikesaKey.IntegInfo = integ.StrToType("AUTH_HMAC_SHA1_96") + // Pseudorandom function is nil + err = ikesaKey.GenerateKeyForIKESA(concatenatedNonce, diffieHellmanSharedKey, + initiatorSPI, responderSPI) + require.Error(t, err) + + ikesaKey.PrfInfo = prf.StrToType("PRF_HMAC_SHA1") + // Diffie-Hellman group is nil + err = ikesaKey.GenerateKeyForIKESA(concatenatedNonce, diffieHellmanSharedKey, + initiatorSPI, responderSPI) + require.Error(t, err) + + ikesaKey.DhInfo = dh.StrToType("DH_2048_BIT_MODP") + // Concatenated nonce is nil + err = ikesaKey.GenerateKeyForIKESA(nil, diffieHellmanSharedKey, + initiatorSPI, responderSPI) + require.Error(t, err) + + // Diffie-Hellman shared key is nil + err = ikesaKey.GenerateKeyForIKESA(concatenatedNonce, nil, + initiatorSPI, responderSPI) + require.Error(t, err) + + // Normal case + err = ikesaKey.GenerateKeyForIKESA(concatenatedNonce, diffieHellmanSharedKey, + initiatorSPI, responderSPI) + require.NoError(t, err) + + expectedSK_ai, err := hex.DecodeString("58a17edd463b4b5062359c1c98b1736d80219691") + require.NoError(t, err) + expectedInteg_i := ikesaKey.IntegInfo.Init(expectedSK_ai) + + expectedSK_ar, err := hex.DecodeString("eb2e18e9a8f9643ea0d0107a28cf5947ecd1597e") + require.NoError(t, err) + ecpectedInteg_r := ikesaKey.IntegInfo.Init(expectedSK_ar) + + expectedSK_ei, err := hex.DecodeString("3dcbcbb2d71d1806d5e5356a5600727eb482101de1868ae9cf71c4117d22cddb") + require.NoError(t, err) + ecpectedEncr_i, err := ikesaKey.EncrInfo.NewCrypto(expectedSK_ei) + require.NoError(t, err) + + expectedSK_er, err := hex.DecodeString("ba3b43cf173435c449f3098c01944f2d9a66c2ca1d967f06a69f36e945a4754b") + require.NoError(t, err) + ecpectedEncr_r, err := ikesaKey.EncrInfo.NewCrypto(expectedSK_er) + require.NoError(t, err) + + expectedSK_pi, err := hex.DecodeString("aff4def6c9113c6942f31fa2d8b74f6c054e0e73") + require.NoError(t, err) + ecpectedPrf_i := ikesaKey.PrfInfo.Init(expectedSK_pi) + + expectedSK_pr, err := hex.DecodeString("c06bd0c0dd3e0b3f9c5b4cbe35c88fdd3948430f") + require.NoError(t, err) + ecpectedPrf_r := ikesaKey.PrfInfo.Init(expectedSK_pr) + + expectedSK_d, err := hex.DecodeString("276e1a8f0d65dae5309da66277ff7c82d39a8956") + require.NoError(t, err) + expectedPrf_d := ikesaKey.PrfInfo.Init(expectedSK_d) + + require.Equal(t, expectedPrf_d, ikesaKey.Prf_d, "SK_d does not match expected value") + require.Equal(t, expectedInteg_i, ikesaKey.Integ_i, "SK_ai does not match expected value") + require.Equal(t, ecpectedInteg_r, ikesaKey.Integ_r, "SK_ar does not match expected value") + require.Equal(t, ecpectedEncr_i, ikesaKey.Encr_i, "SK_ei does not match expected value") + require.Equal(t, ecpectedEncr_r, ikesaKey.Encr_r, "SK_er does not match expected value") + require.Equal(t, ecpectedPrf_i, ikesaKey.Prf_i, "SK_pi does not match expected value") + require.Equal(t, ecpectedPrf_r, ikesaKey.Prf_r, "SK_pr does not match expected value") +} - if childsa.dhInfo != nil || childsa.encrKInfo != encrKType3 || - childsa.integKInfo != nil || childsa.esnInfo != esnType2 { - t.Fatal("SelectProposal selected a false result") - } +func TestGenerateKeyForChildSA(t *testing.T) { + // IKE Security Association is nil + childSAKey := &ChildSAKey{} + err := childSAKey.GenerateKeyForChildSA(nil, nil) + require.Error(t, err) + + ikeSAKey := &IKESAKey{} + + // Child SecurityAssociation is nil + var c *ChildSAKey + err = c.GenerateKeyForChildSA(ikeSAKey, nil) + require.Error(t, err) + + // Pseudorandom function is nil + err = childSAKey.GenerateKeyForChildSA(ikeSAKey, nil) + require.Error(t, err) + + ikeSAKey.PrfInfo = prf.StrToType("PRF_HMAC_SHA1") + + // Encryption algorithm is nil + err = childSAKey.GenerateKeyForChildSA(ikeSAKey, nil) + require.Error(t, err) + + childSAKey.EncrKInfo = encr.StrToKType("ENCR_AES_CBC_256") + childSAKey.IntegKInfo = integ.StrToKType("AUTH_HMAC_SHA1_96") + + // Deriving key is nil + err = childSAKey.GenerateKeyForChildSA(ikeSAKey, nil) + require.Error(t, err) + + sk_d, err := hex.DecodeString("276e1a8f0d65dae5309da66277ff7c82d39a8956") + require.NoError(t, err) + ikeSAKey.Prf_d = ikeSAKey.PrfInfo.Init(sk_d) + + err = childSAKey.GenerateKeyForChildSA(ikeSAKey, nil) + require.NoError(t, err) + + expectedInitiatorToResponderEncryptionKey, err := hex.DecodeString( + "8adf11fb9c3d575f9aff5ce58c4891533c44026dc537d68dcc8c08d453e9e6df") + require.NoError(t, err) + expectedInitiatorToResponderIntegrityKey, err := hex.DecodeString( + "1a04be51ae650581a546411d2dbe09507e49329f") + require.NoError(t, err) + expectedResponderToInitiatorEncryptionKey, err := hex.DecodeString( + "103318186d2f7837e2d8a28cf375c2552634bd610f5142f30dfb223892cdca13") + require.NoError(t, err) + expectedResponderToInitiatorIntegrityKey, err := hex.DecodeString( + "f3e8e1d3b1e0e1ce731b0d4f84dc05ac9456454c") + require.NoError(t, err) + + require.Equal(t, expectedInitiatorToResponderEncryptionKey, + childSAKey.InitiatorToResponderEncryptionKey, "InitiatorToResponderEncryptionKey does not match expected value") + require.Equal(t, expectedInitiatorToResponderIntegrityKey, + childSAKey.InitiatorToResponderIntegrityKey, "InitiatorToResponderIntegrityKey does not match expected value") + require.Equal(t, expectedResponderToInitiatorEncryptionKey, + childSAKey.ResponderToInitiatorEncryptionKey, "ResponderToInitiatorEncryptionKey does not match expected value") + require.Equal(t, expectedResponderToInitiatorIntegrityKey, + childSAKey.ResponderToInitiatorIntegrityKey, "ResponderToInitiatorIntegrityKey does not match expected value") } func TestChildToProposal(t *testing.T) { dhType := dh.StrToType("DH_1024_BIT_MODP") encrKType := encr.StrToKType("ENCR_AES_CBC_256") integKType := integ.StrToKType("AUTH_HMAC_MD5_96") - esnType := esn.StrToType("ESN_ENABLE") + esnType, err := esn.StrToType("ESN_ENABLE") + require.NoError(t, err) - childsa := ChildSA{ - dhInfo: dhType, - encrKInfo: encrKType, - integKInfo: integKType, - esnInfo: esnType, + childsaKey := ChildSAKey{ + DhInfo: dhType, + EncrKInfo: encrKType, + IntegKInfo: integKType, + EsnInfo: esnType, } - proposal := childsa.ToProposal() + proposal, err := childsaKey.ToProposal() + require.NoError(t, err) if len(proposal.DiffieHellmanGroup) != 1 || len(proposal.EncryptionAlgorithm) != 1 || @@ -546,23 +305,24 @@ func TestChildSetProposal(t *testing.T) { dhType := dh.StrToType("DH_1024_BIT_MODP") encrKType := encr.StrToKType("ENCR_AES_CBC_256") integKType := integ.StrToKType("AUTH_HMAC_MD5_96") - esnType := esn.StrToType("ESN_ENABLE") + esnType, err := esn.StrToType("ESN_ENABLE") + require.NoError(t, err) proposal := new(message.Proposal) proposal.DiffieHellmanGroup = append(proposal.DiffieHellmanGroup, dh.ToTransform(dhType)) - proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, encr.ToTransformChildSA(encrKType)) + encrKTranform, err := encr.ToTransformChildSA(encrKType) + require.NoError(t, err) + proposal.EncryptionAlgorithm = append(proposal.EncryptionAlgorithm, encrKTranform) proposal.IntegrityAlgorithm = append(proposal.IntegrityAlgorithm, integ.ToTransformChildSA(integKType)) proposal.ExtendedSequenceNumbers = append(proposal.ExtendedSequenceNumbers, esn.ToTransform(esnType)) - childsa := new(ChildSA) - - childsa.SetProposal(proposal) + childsaKey, err := NewChildSAKeyByProposal(proposal) + require.NoError(t, err) - if childsa.dhInfo == nil || - childsa.encrKInfo == nil || - childsa.integKInfo == nil || - childsa.esnInfo == nil { + if childsaKey.DhInfo == nil || + childsaKey.EncrKInfo == nil || + childsaKey.IntegKInfo == nil { t.FailNow() } }