diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..05fa7d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Toolchain +# Goland project folder +.idea/ +# Visual Studio Code +.vscode/ +# emacs/vim +GPATH +GRTAGS +GTAGS +TAGS +tags +cscope.* +# mac +.DS_Store + diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..089d56a --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,301 @@ +# This file contains all available configuration options +# with their default values. +# options for analysis running +run: + # default concurrency is a available CPU number + concurrency: 4 + # timeout for analysis, e.g. 30s, 5m, default is 1m + timeout: 1m + # 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: false + # 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: +<<<<<<< HEAD +======= + - release + - lib/openapi + - lib/ngap/ngapType + - ngapType + - lib/nas/nasType + - nasType + - lib/nas/nasMessage + - nasMessage + - lib/CommonConsumerTestData +>>>>>>> 8d5e6d9... feat(aper): lib go modulize + # 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: + - "api_.*\\.go$" + - "model_.*\\.go$" + - "routers.go" + - "client.go" + - "configuration.go" + - "nas.go" + # by default isn't set. 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 + # not need updates, such as in a continuous integration and testing system. + # 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 + # Allow multiple parallel golangci-lint instances running. + # If false (default) - golangci-lint acquires file lock on start. + allow-parallel-runners: true +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" + format: colored-line-number + # 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 +# all available settings of specific linters +linters-settings: + errcheck: + # report about not checking of errors in type assertions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: false + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: true + # [deprecated] comma-separated list of pairs of the form pkg:regex + # the regex is used to ignore names within pkg. (default "fmt:.*"). + # see https://github.com/kisielk/errcheck#the-deprecated-method for details + #ignore: fmt:.*,io/ioutil:^Read.* + # 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-occurrences: 3 + 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: + #- rangeValCopy + # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty + disabled-checks: + - regexpMust + # 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: + - performance + disabled-tags: + - experimental + settings: # settings passed to gocritic + captLocal: # must be valid enabled check name + paramsOnly: true + rangeValCopy: + sizeThreshold: 32 + gocyclo: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 10 + 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 + keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting + #- TODO + - FIXME + - BUG + #- NOTE + #- 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 + # settings per analyzer + settings: + printf: # analyzer name, run `go tool vet help` to see all analyzers + 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 + 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" + 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 + 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 + custom: + # Each custom linter should have a unique name. + +linters: + enable: + - gofmt + - govet + - errcheck + - staticcheck + - unused + - gosimple + - structcheck + - varcheck + - ineffassign + - deadcode + - typecheck + # Additional + - lll + - godox + # - gomnd + #- maligned + #- nestif + #- goconst + #- gocognit + - nakedret + #disable-all: 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: + # Excluding configuration per-path, per-linter, per-text and per-source + 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 + # excluded by default patterns execute `golangci-lint run --help`. + # Default value for this option is true. + exclude-use-default: false + # The default value is false. If set to true exclude and exclude-rules + # 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: + #- 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 + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + #max-same-issues: 0 + # Show only new issues: if there are unstaged changes or untracked files, + # only those changes are analyzed, else only changes in HEAD~ are analyzed. + # It's a super-useful option for integration of golangci-lint into existing + # large codebase. It's not practical to fix all existing issues at the moment + # of integration: much better don't allow issues in new code. + # Default is false. + new: false + # Show only new issues created after git revision `REV` + 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. + # - 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 + 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. + # Only affects out formats that support setting severity information. + rules: + - linters: + - gomnd + severity: ignore diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f465a20 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Change Log +--- +2020-03-xx-xx +--- +- Implemented enchacements: + +- Fixed bugs: + +- Closed issues: diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..4124f39 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Communication Service/Software Laboratory, National Chiao Tung University + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/aper.go b/aper.go new file mode 100644 index 0000000..34b83b5 --- /dev/null +++ b/aper.go @@ -0,0 +1,897 @@ +package aper + +import ( + "fmt" + "path" + "reflect" + "runtime" + + "github.com/free5gc/aper/logger" +) + +type perBitData struct { + bytes []byte + byteOffset uint64 + bitsOffset uint +} + +func perTrace(level int, s string) { + + _, file, line, ok := runtime.Caller(1) + if !ok { + logger.AperLog.Debugln(s) + } else { + logger.AperLog.Debugf("%s (%s:%d)\n", s, path.Base(file), line) + } +} + +func perBitLog(numBits uint64, byteOffset uint64, bitsOffset uint, value interface{}) string { + if reflect.TypeOf(value).Kind() == reflect.Uint64 { + return fmt.Sprintf(" [PER got %2d bits, byteOffset(after): %d, bitsOffset(after): %d, value: 0x%0x]", + numBits, byteOffset, bitsOffset, reflect.ValueOf(value).Uint()) + } + return fmt.Sprintf(" [PER got %2d bits, byteOffset(after): %d, bitsOffset(after): %d, value: 0x%0x]", + numBits, byteOffset, bitsOffset, reflect.ValueOf(value).Bytes()) + +} + +// GetBitString is to get BitString with desire size from source byte array with bit offset +func GetBitString(srcBytes []byte, bitsOffset uint, numBits uint) (dstBytes []byte, err error) { + bitsLeft := uint(len(srcBytes))*8 - bitsOffset + if numBits > bitsLeft { + err = fmt.Errorf("Get bits overflow, requireBits: %d, leftBits: %d", numBits, bitsLeft) + return + } + byteLen := (bitsOffset + numBits + 7) >> 3 + numBitsByteLen := (numBits + 7) >> 3 + dstBytes = make([]byte, numBitsByteLen) + numBitsMask := byte(0xff) + if modEight := numBits & 0x7; modEight != 0 { + numBitsMask <<= uint8(8 - (modEight)) + } + for i := 1; i < int(byteLen); i++ { + dstBytes[i-1] = srcBytes[i-1]<>(8-bitsOffset) + } + if byteLen == numBitsByteLen { + dstBytes[byteLen-1] = srcBytes[byteLen-1] << bitsOffset + } + dstBytes[numBitsByteLen-1] &= numBitsMask + return +} + +// GetFewBits is to get Value with desire few bits from source byte with bit offset +// func GetFewBits(srcByte byte, bitsOffset uint, numBits uint) (value uint64, err error) { + +// if numBits == 0 { +// value = 0 +// return +// } +// bitsLeft := 8 - bitsOffset +// if bitsLeft < numBits { +// err = fmt.Errorf("Get bits overflow, requireBits: %d, leftBits: %d", numBits, bitsLeft) +// return +// } +// if bitsOffset == 0 { +// value = uint64(srcByte >> (8 - numBits)) +// } else { +// value = uint64((srcByte << bitsOffset) >> (8 - numBits)) +// } +// return +// } + +// GetBitsValue is to get Value with desire bits from source byte array with bit offset +func GetBitsValue(srcBytes []byte, bitsOffset uint, numBits uint) (value uint64, err error) { + var dstBytes []byte + dstBytes, err = GetBitString(srcBytes, bitsOffset, numBits) + if err != nil { + return + } + for i, j := 0, numBits; j >= 8; i, j = i+1, j-8 { + value <<= 8 + value |= uint64(uint(dstBytes[i])) + } + if numBitsOff := uint(numBits & 0x7); numBitsOff != 0 { + var mask uint = (1 << numBitsOff) - 1 + value <<= numBitsOff + value |= uint64(uint(dstBytes[len(dstBytes)-1]>>(8-numBitsOff)) & mask) + } + return +} + +func (pd *perBitData) bitCarry() { + pd.byteOffset += uint64(pd.bitsOffset >> 3) + pd.bitsOffset = pd.bitsOffset & 0x07 +} + +func (pd *perBitData) getBitString(numBits uint) (dstBytes []byte, err error) { + + dstBytes, err = GetBitString(pd.bytes[pd.byteOffset:], pd.bitsOffset, numBits) + if err != nil { + return + } + pd.bitsOffset += uint(numBits) + + pd.bitCarry() + perTrace(1, perBitLog(uint64(numBits), pd.byteOffset, pd.bitsOffset, dstBytes)) + return +} + +func (pd *perBitData) getBitsValue(numBits uint) (value uint64, err error) { + value, err = GetBitsValue(pd.bytes[pd.byteOffset:], pd.bitsOffset, numBits) + if err != nil { + return + } + pd.bitsOffset += numBits + pd.bitCarry() + perTrace(1, perBitLog(uint64(numBits), pd.byteOffset, pd.bitsOffset, value)) + return +} + +func (pd *perBitData) parseAlignBits() error { + + if (pd.bitsOffset & 0x7) > 0 { + alignBits := 8 - ((pd.bitsOffset) & 0x7) + perTrace(2, fmt.Sprintf("Aligning %d bits", alignBits)) + if val, err := pd.getBitsValue(alignBits); err != nil { + return err + } else if val != 0 { + return fmt.Errorf("Align Bit is not zero") + } + } else if pd.bitsOffset != 0 { + pd.bitCarry() + } + return nil +} + +func (pd *perBitData) parseConstraintValue(valueRange int64) (value uint64, err error) { + perTrace(3, fmt.Sprintf("Getting Constraint Value with range %d", valueRange)) + + var bytes uint + if valueRange <= 255 { + if valueRange < 0 { + err = fmt.Errorf("Value range is negative") + return + } + var i uint + // 1 ~ 8 bits + for i = 1; i <= 8; i++ { + upper := 1 << i + if int64(upper) >= valueRange { + break + } + } + value, err = pd.getBitsValue(i) + return + } else if valueRange == 256 { + bytes = 1 + } else if valueRange <= 65536 { + bytes = 2 + } else { + err = fmt.Errorf("Constraint Value is large than 65536") + return + } + if err = pd.parseAlignBits(); err != nil { + return + } + value, err = pd.getBitsValue(bytes * 8) + return value, err +} + +func (pd *perBitData) parseLength(sizeRange int64, repeat *bool) (value uint64, err error) { + *repeat = false + if sizeRange <= 65536 && sizeRange > 0 { + return pd.parseConstraintValue(sizeRange) + } + + if err = pd.parseAlignBits(); err != nil { + return + } + firstByte, err := pd.getBitsValue(8) + if err != nil { + return + } + if (firstByte & 128) == 0 { // #10.9.3.6 + value = firstByte & 0x7F + return + } else if (firstByte & 64) == 0 { // #10.9.3.7 + var secondByte uint64 + if secondByte, err = pd.getBitsValue(8); err != nil { + return + } + value = ((firstByte & 63) << 8) | secondByte + return + } + firstByte &= 63 + if firstByte < 1 || firstByte > 4 { + err = fmt.Errorf("Parse Length Out of Constraint") + return + } + *repeat = true + value = 16384 * firstByte + return value, err +} + +func (pd *perBitData) parseBitString(extensed bool, lowerBoundPtr *int64, upperBoundPtr *int64) (BitString, error) { + var lb, ub, sizeRange int64 = 0, -1, -1 + if !extensed { + if lowerBoundPtr != nil { + lb = *lowerBoundPtr + } + if upperBoundPtr != nil { + ub = *upperBoundPtr + sizeRange = ub - lb + 1 + } + } + if ub > 65535 { + sizeRange = -1 + } + // initailization + bitString := BitString{[]byte{}, 0} + // lowerbound == upperbound + if sizeRange == 1 { + sizes := uint64(ub+7) >> 3 + bitString.BitLength = uint64(ub) + perTrace(2, fmt.Sprintf("Decoding BIT STRING size %d", ub)) + if sizes > 2 { + if err := pd.parseAlignBits(); err != nil { + return bitString, err + } + if (pd.byteOffset + sizes) > uint64(len(pd.bytes)) { + err := fmt.Errorf("PER data out of range") + return bitString, err + } + bitString.Bytes = pd.bytes[pd.byteOffset : pd.byteOffset+sizes] + pd.byteOffset += sizes + pd.bitsOffset = uint(ub & 0x7) + if pd.bitsOffset > 0 { + pd.byteOffset-- + } + perTrace(1, perBitLog(uint64(ub), pd.byteOffset, pd.bitsOffset, bitString.Bytes)) + } else { + if byte, err := pd.getBitString(uint(ub)); err != nil { + logger.AperLog.Warnf("PD GetBitString error: %+v", err) + return bitString, err + } else { + bitString.Bytes = byte + } + } + perTrace(2, fmt.Sprintf("Decoded BIT STRING (length = %d): %0.8b", ub, bitString.Bytes)) + return bitString, nil + + } + repeat := false + for { + var rawLength uint64 + if length, err := pd.parseLength(sizeRange, &repeat); err != nil { + return bitString, err + } else { + rawLength = length + } + rawLength += uint64(lb) + perTrace(2, fmt.Sprintf("Decoding BIT STRING size %d", rawLength)) + if rawLength == 0 { + return bitString, nil + } + sizes := (rawLength + 7) >> 3 + if err := pd.parseAlignBits(); err != nil { + return bitString, err + } + + if (pd.byteOffset + sizes) > uint64(len(pd.bytes)) { + err := fmt.Errorf("PER data out of range") + return bitString, err + } + bitString.Bytes = append(bitString.Bytes, pd.bytes[pd.byteOffset:pd.byteOffset+sizes]...) + bitString.BitLength += rawLength + pd.byteOffset += sizes + pd.bitsOffset = uint(rawLength & 0x7) + if pd.bitsOffset != 0 { + pd.byteOffset-- + } + perTrace(1, perBitLog(rawLength, pd.byteOffset, pd.bitsOffset, bitString.Bytes)) + perTrace(2, fmt.Sprintf("Decoded BIT STRING (length = %d): %0.8b", rawLength, bitString.Bytes)) + + if !repeat { + // if err = pd.parseAlignBits(); err != nil { + // return + // } + break + } + } + return bitString, nil +} +func (pd *perBitData) parseOctetString(extensed bool, lowerBoundPtr *int64, upperBoundPtr *int64) ( + OctetString, error) { + var lb, ub, sizeRange int64 = 0, -1, -1 + if !extensed { + if lowerBoundPtr != nil { + lb = *lowerBoundPtr + } + if upperBoundPtr != nil { + ub = *upperBoundPtr + sizeRange = ub - lb + 1 + } + } + if ub > 65535 { + sizeRange = -1 + } + // initailization + octetString := OctetString("") + // lowerbound == upperbound + if sizeRange == 1 { + perTrace(2, fmt.Sprintf("Decoding OCTET STRING size %d", ub)) + if ub > 2 { + unsignedUB := uint64(ub) + if err := pd.parseAlignBits(); err != nil { + return octetString, err + } + if (int64(pd.byteOffset) + ub) > int64(len(pd.bytes)) { + err := fmt.Errorf("per data out of range") + return octetString, err + } + octetString = pd.bytes[pd.byteOffset : pd.byteOffset+unsignedUB] + pd.byteOffset += uint64(ub) + perTrace(1, perBitLog(8*unsignedUB, pd.byteOffset, pd.bitsOffset, octetString)) + } else { + if octet, err := pd.getBitString(uint(ub * 8)); err != nil { + return octetString, err + } else { + octetString = octet + } + } + perTrace(2, fmt.Sprintf("Decoded OCTET STRING (length = %d): 0x%0x", ub, octetString)) + return octetString, nil + + } + repeat := false + for { + var rawLength uint64 + if length, err := pd.parseLength(sizeRange, &repeat); err != nil { + return octetString, err + } else { + rawLength = length + } + rawLength += uint64(lb) + perTrace(2, fmt.Sprintf("Decoding OCTET STRING size %d", rawLength)) + if rawLength == 0 { + return octetString, nil + } else if err := pd.parseAlignBits(); err != nil { + return octetString, err + } + if (rawLength + pd.byteOffset) > uint64(len(pd.bytes)) { + err := fmt.Errorf("per data out of range ") + return octetString, err + } + octetString = append(octetString, pd.bytes[pd.byteOffset:pd.byteOffset+rawLength]...) + pd.byteOffset += rawLength + perTrace(1, perBitLog(8*rawLength, pd.byteOffset, pd.bitsOffset, octetString)) + perTrace(2, fmt.Sprintf("Decoded OCTET STRING (length = %d): 0x%0x", rawLength, octetString)) + if !repeat { + // if err = pd.parseAlignBits(); err != nil { + // return + // } + break + } + } + return octetString, nil +} + +func (pd *perBitData) parseBool() (value bool, err error) { + perTrace(3, "Decoding BOOLEAN Value") + bit, err1 := pd.getBitsValue(1) + if err1 != nil { + err = err1 + return + } + if bit == 1 { + value = true + perTrace(2, "Decoded BOOLEAN Value : ture") + } else { + value = false + perTrace(2, "Decoded BOOLEAN Value : false") + } + return +} + +func (pd *perBitData) parseInteger(extensed bool, lowerBoundPtr *int64, upperBoundPtr *int64) (int64, error) { + var lb, ub, valueRange int64 = 0, -1, 0 + if !extensed { + if lowerBoundPtr == nil { + perTrace(3, "Decoding INTEGER with Unconstraint Value") + valueRange = -1 + } else { + lb = *lowerBoundPtr + if upperBoundPtr != nil { + ub = *upperBoundPtr + valueRange = ub - lb + 1 + perTrace(3, fmt.Sprintf("Decoding INTEGER with Value Range(%d..%d)", lb, ub)) + } else { + perTrace(3, fmt.Sprintf("Decoding INTEGER with Semi-Constraint Range(%d..)", lb)) + } + } + } else { + valueRange = -1 + perTrace(3, "Decoding INTEGER with Extensive Value") + } + var rawLength uint + if valueRange == 1 { + return ub, nil + } else if valueRange <= 0 { + // semi-constraint or unconstraint + if err := pd.parseAlignBits(); err != nil { + return int64(0), err + } + if pd.byteOffset >= uint64(len(pd.bytes)) { + return int64(0), fmt.Errorf("per data out of range") + } + rawLength = uint(pd.bytes[pd.byteOffset]) + pd.byteOffset++ + perTrace(1, perBitLog(8, pd.byteOffset, pd.bitsOffset, uint64(rawLength))) + } else if valueRange <= 65536 { + rawValue, err := pd.parseConstraintValue(valueRange) + if err != nil { + return int64(0), err + } else { + return int64(rawValue) + lb, nil + } + } else { + // valueRange > 65536 + var byteLen uint + unsignedValueRange := uint64(valueRange - 1) + for byteLen = 1; byteLen <= 127; byteLen++ { + unsignedValueRange >>= 8 + if unsignedValueRange == 0 { + break + } + } + var i, upper uint + // 1 ~ 8 bits + for i = 1; i <= 8; i++ { + upper = 1 << i + if upper >= byteLen { + break + } + } + if tempLength, err := pd.getBitsValue(i); err != nil { + return int64(0), err + } else { + rawLength = uint(tempLength) + } + rawLength++ + if err := pd.parseAlignBits(); err != nil { + return int64(0), err + } + } + perTrace(2, fmt.Sprintf("Decoding INTEGER Length with %d bytes", rawLength)) + + if rawValue, err := pd.getBitsValue(rawLength * 8); err != nil { + return int64(0), err + } else if valueRange < 0 { + signedBitMask := uint64(1 << (rawLength*8 - 1)) + valueMask := signedBitMask - 1 + // negative + if rawValue&signedBitMask > 0 { + return int64((^rawValue)&valueMask+1) * -1, nil + } + return int64(rawValue) + lb, nil + } else { + return int64(rawValue) + lb, nil + } +} + +// parse ENUMERATED type but do not implement extensive value and different value with index +func (pd *perBitData) parseEnumerated(extensed bool, lowerBoundPtr *int64, upperBoundPtr *int64) (value uint64, + err error) { + if extensed { + err = fmt.Errorf("Unsupport the extensive value of ENUMERATED ") + return + } + if lowerBoundPtr == nil || upperBoundPtr == nil { + err = fmt.Errorf("ENUMERATED value constraint is error ") + return + } + lb, ub := *lowerBoundPtr, *upperBoundPtr + valueRange := ub - lb + 1 + perTrace(2, fmt.Sprintf("Decoding ENUMERATED with Value Range(%d..%d)", lb, ub)) + if valueRange > 1 { + value, err = pd.parseConstraintValue(valueRange) + } + perTrace(2, fmt.Sprintf("Decoded ENUMERATED Value : %d", value)) + return + +} + +func (pd *perBitData) parseSequenceOf(sizeExtensed bool, params fieldParameters, sliceType reflect.Type) ( + reflect.Value, error) { + var sliceContent reflect.Value + var lb int64 = 0 + var sizeRange int64 + if params.sizeLowerBound != nil && *params.sizeLowerBound < 65536 { + lb = *params.sizeLowerBound + } + if !sizeExtensed && params.sizeUpperBound != nil && *params.sizeUpperBound < 65536 { + ub := *params.sizeUpperBound + sizeRange = ub - lb + 1 + perTrace(3, fmt.Sprintf("Decoding Length of \"SEQUENCE OF\" with Size Range(%d..%d)", lb, ub)) + } else { + sizeRange = -1 + perTrace(3, fmt.Sprintf("Decoding Length of \"SEQUENCE OF\" with Semi-Constraint Range(%d..)", lb)) + } + + var numElements uint64 + if sizeRange > 1 { + if numElementsTmp, err := pd.parseConstraintValue(sizeRange); err != nil { + logger.AperLog.Warnf("Parse Constraint Value failed: %+v", err) + } else { + numElements = numElementsTmp + } + numElements += uint64(lb) + } else if sizeRange == 1 { + numElements += uint64(lb) + } else { + if err := pd.parseAlignBits(); err != nil { + return sliceContent, err + } + if pd.byteOffset >= uint64(len(pd.bytes)) { + err := fmt.Errorf("per data out of range") + return sliceContent, err + } + numElements = uint64(pd.bytes[pd.byteOffset]) + pd.byteOffset++ + perTrace(1, perBitLog(8, pd.byteOffset, pd.bitsOffset, numElements)) + } + perTrace(2, fmt.Sprintf("Decoding \"SEQUENCE OF\" struct %s with len(%d)", sliceType.Elem().Name(), numElements)) + params.sizeExtensible = false + params.sizeUpperBound = nil + params.sizeLowerBound = nil + intNumElements := int(numElements) + sliceContent = reflect.MakeSlice(sliceType, intNumElements, intNumElements) + for i := 0; i < intNumElements; i++ { + err := parseField(sliceContent.Index(i), pd, params) + if err != nil { + return sliceContent, err + } + } + return sliceContent, nil +} + +func (pd *perBitData) getChoiceIndex(extensed bool, upperBoundPtr *int64) (present int, err error) { + if extensed { + err = fmt.Errorf("Unsupport value of CHOICE type is in Extensed") + } else if upperBoundPtr == nil { + err = fmt.Errorf("The upper bound of CHIOCE is missing") + } else if ub := *upperBoundPtr; ub < 0 { + err = fmt.Errorf("The upper bound of CHIOCE is negative") + } else if rawChoice, err1 := pd.parseConstraintValue(ub + 1); err1 != nil { + err = err1 + } else { + perTrace(2, fmt.Sprintf("Decoded Present index of CHOICE is %d + 1", rawChoice)) + present = int(rawChoice) + 1 + } + return +} +func getReferenceFieldValue(v reflect.Value) (value int64, err error) { + fieldType := v.Type() + switch v.Kind() { + case reflect.Int, reflect.Int32, reflect.Int64: + value = v.Int() + case reflect.Struct: + if fieldType.Field(0).Name == "Present" { + present := int(v.Field(0).Int()) + if present == 0 { + err = fmt.Errorf("ReferenceField Value present is 0(present's field number)") + } else if present >= fieldType.NumField() { + err = fmt.Errorf("Present is bigger than number of struct field") + } else { + value, err = getReferenceFieldValue(v.Field(present)) + } + } else { + value, err = getReferenceFieldValue(v.Field(0)) + } + default: + err = fmt.Errorf("OpenType reference only support INTEGER") + } + return +} + +func (pd *perBitData) parseOpenType(v reflect.Value, params fieldParameters) error { + + pdOpenType := &perBitData{[]byte(""), 0, 0} + repeat := false + for { + var rawLength uint64 + if rawLengthTmp, err := pd.parseLength(-1, &repeat); err != nil { + return err + } else { + rawLength = rawLengthTmp + } + if rawLength == 0 { + break + } else if err := pd.parseAlignBits(); err != nil { + return err + } + if (rawLength + pd.byteOffset) > uint64(len(pd.bytes)) { + return fmt.Errorf("per data out of range ") + } + pdOpenType.bytes = append(pdOpenType.bytes, pd.bytes[pd.byteOffset:pd.byteOffset+rawLength]...) + pd.byteOffset += rawLength + + if !repeat { + if err := pd.parseAlignBits(); err != nil { + return err + } + break + } + } + perTrace(2, fmt.Sprintf("Decoding OpenType %s with (len = %d byte)", v.Type().String(), len(pdOpenType.bytes))) + err := parseField(v, pdOpenType, params) + perTrace(2, fmt.Sprintf("Decoded OpenType %s", v.Type().String())) + return err +} + +// parseField is the main parsing function. Given a byte slice and an offset +// into the array, it will try to parse a suitable ASN.1 value out and store it +// in the given Value. TODO : ObjectIdenfier, handle extension Field +func parseField(v reflect.Value, pd *perBitData, params fieldParameters) error { + fieldType := v.Type() + + // If we have run out of data return error. + if pd.byteOffset == uint64(len(pd.bytes)) { + return fmt.Errorf("sequence truncated") + } + if v.Kind() == reflect.Ptr { + ptr := reflect.New(fieldType.Elem()) + v.Set(ptr) + return parseField(v.Elem(), pd, params) + } + sizeExtensible := false + valueExtensible := false + if params.sizeExtensible { + if bitsValue, err1 := pd.getBitsValue(1); err1 != nil { + return err1 + } else if bitsValue != 0 { + sizeExtensible = true + } + perTrace(2, fmt.Sprintf("Decoded Size Extensive Bit : %t", sizeExtensible)) + } + if params.valueExtensible && v.Kind() != reflect.Slice { + if bitsValue, err1 := pd.getBitsValue(1); err1 != nil { + return err1 + } else if bitsValue != 0 { + valueExtensible = true + } + perTrace(2, fmt.Sprintf("Decoded Value Extensive Bit : %t", valueExtensible)) + } + + // We deal with the structures defined in this package first. + switch fieldType { + case BitStringType: + bitString, err1 := pd.parseBitString(sizeExtensible, params.sizeLowerBound, params.sizeUpperBound) + + if err1 != nil { + return err1 + } + v.Set(reflect.ValueOf(bitString)) + return nil + case ObjectIdentifierType: + return fmt.Errorf("Unsupport ObjectIdenfier type") + case OctetStringType: + if octetString, err := pd.parseOctetString(sizeExtensible, params.sizeLowerBound, params.sizeUpperBound); err != nil { + return err + } else { + v.Set(reflect.ValueOf(octetString)) + return nil + } + case EnumeratedType: + if parsedEnum, err := pd.parseEnumerated(valueExtensible, params.valueLowerBound, + params.valueUpperBound); err != nil { + return err + } else { + v.SetUint(parsedEnum) + return nil + } + } + switch val := v; val.Kind() { + case reflect.Bool: + if parsedBool, err := pd.parseBool(); err != nil { + return err + } else { + val.SetBool(parsedBool) + return nil + } + case reflect.Int, reflect.Int32, reflect.Int64: + if parsedInt, err := pd.parseInteger(valueExtensible, params.valueLowerBound, params.valueUpperBound); err != nil { + return err + } else { + val.SetInt(parsedInt) + perTrace(2, fmt.Sprintf("Decoded INTEGER Value: %d", parsedInt)) + return nil + } + case reflect.Struct: + + structType := fieldType + var structParams []fieldParameters + var optionalCount uint + var optionalPresents uint64 + + // pass tag for optional + for i := 0; i < structType.NumField(); i++ { + if structType.Field(i).PkgPath != "" { + return fmt.Errorf("struct contains unexported fields : " + structType.Field(i).PkgPath) + } + tempParams := parseFieldParameters(structType.Field(i).Tag.Get("aper")) + // for optional flag + if tempParams.optional { + optionalCount++ + } + structParams = append(structParams, tempParams) + } + + if optionalCount > 0 { + if optionalPresentsTmp, err := pd.getBitsValue(optionalCount); err != nil { + return err + } else { + optionalPresents = optionalPresentsTmp + } + perTrace(2, fmt.Sprintf("optionalPresents is %0b", optionalPresents)) + } + + // CHOICE or OpenType + if structType.NumField() > 0 && structType.Field(0).Name == "Present" { + var present int = 0 + if params.openType { + if params.referenceFieldValue == nil { + return fmt.Errorf("OpenType reference value is empty") + } + refValue := *params.referenceFieldValue + + for j, param := range structParams { + if j == 0 { + continue + } + if param.referenceFieldValue != nil && *param.referenceFieldValue == refValue { + present = j + break + } + } + if present == 0 { + return fmt.Errorf("OpenType reference value does not match any field") + } else if present >= structType.NumField() { + return fmt.Errorf("OpenType Present is bigger than number of struct field") + } else { + val.Field(0).SetInt(int64(present)) + perTrace(2, fmt.Sprintf("Decoded Present index of OpenType is %d ", present)) + return pd.parseOpenType(val.Field(present), structParams[present]) + } + } else { + if presentTmp, err := pd.getChoiceIndex(valueExtensible, params.valueUpperBound); err != nil { + logger.AperLog.Errorf("pd.getChoiceIndex Error") + } else { + present = presentTmp + } + val.Field(0).SetInt(int64(present)) + if present == 0 { + return fmt.Errorf("CHOICE present is 0(present's field number)") + } else if present >= structType.NumField() { + return fmt.Errorf("CHOICE Present is bigger than number of struct field") + } else { + return parseField(val.Field(present), pd, structParams[present]) + } + } + } + + for i := 0; i < structType.NumField(); i++ { + if structParams[i].optional && optionalCount > 0 { + optionalCount-- + if optionalPresents&(1<>3] + if pd.bitsOffset == 0 { + pd.bytes = append(pd.bytes, bytes...) + pd.bitsOffset = (numBits & 0x7) + perTrace(1, perRawBitLog(uint64(numBits), len(pd.bytes), pd.bitsOffset, bytes)) + return + } + bitsLeft := 8 - pd.bitsOffset + currentByte := len(pd.bytes) - 1 + if numBits <= bitsLeft { + pd.bytes[currentByte] |= (bytes[0] >> pd.bitsOffset) + } else { + bytes = append([]byte{0x00}, bytes...) + var shiftBytes []byte + if shiftBytes, err = GetBitString(bytes, bitsLeft, pd.bitsOffset+numBits); err != nil { + return + } + pd.bytes[currentByte] |= shiftBytes[0] + pd.bytes = append(pd.bytes, shiftBytes[1:]...) + bytes = bytes[1:] + } + pd.bitsOffset = (numBits & 0x7) + pd.bitsOffset + pd.bitCarry() + perTrace(1, perRawBitLog(uint64(numBits), len(pd.bytes), pd.bitsOffset, bytes)) + return +} + +func (pd *perRawBitData) putBitsValue(value uint64, numBits uint) (err error) { + if numBits == 0 { + return + } + Byteslen := (numBits + 7) >> 3 + tempBytes := make([]byte, Byteslen) + bitOff := numBits & 0x7 + if bitOff == 0 { + bitOff = 8 + } + LeftbitOff := 8 - bitOff + tempBytes[Byteslen-1] = byte((value << LeftbitOff) & 0xff) + value >>= bitOff + var i int + for i = int(Byteslen) - 2; value > 0; i-- { + if i < 0 { + err = fmt.Errorf("Bits Value is over capacity") + return + } + tempBytes[i] = byte(value & 0xff) + value >>= 8 + } + + err = pd.putBitString(tempBytes, numBits) + return +} + +func (pd *perRawBitData) appendConstraintValue(valueRange int64, value uint64) (err error) { + perTrace(3, fmt.Sprintf("Putting Constraint Value %d with range %d", value, valueRange)) + + var bytes uint + if valueRange <= 255 { + if valueRange < 0 { + err = fmt.Errorf("Value range is negative") + return + } + var i uint + // 1 ~ 8 bits + for i = 1; i <= 8; i++ { + upper := 1 << i + if int64(upper) >= valueRange { + break + } + } + err = pd.putBitsValue(value, i) + return + } else if valueRange == 256 { + bytes = 1 + } else if valueRange <= 65536 { + bytes = 2 + } else { + err = fmt.Errorf("Constraint Value is large than 65536") + return + } + pd.appendAlignBits() + err = pd.putBitsValue(value, bytes*8) + return +} + +func (pd *perRawBitData) appendLength(sizeRange int64, value uint64) (err error) { + if sizeRange <= 65536 && sizeRange > 0 { + return pd.appendConstraintValue(sizeRange, value) + } + pd.appendAlignBits() + perTrace(2, fmt.Sprintf("Putting Length of Value : %d", value)) + if value <= 127 { + err = pd.putBitsValue(value, 8) + return + } else if value <= 16383 { + value |= 0x8000 + err = pd.putBitsValue(value, 16) + return + } + + value = (value >> 14) | 0xc0 + err = pd.putBitsValue(value, 8) + return +} + +func (pd *perRawBitData) appendBitString(bytes []byte, bitsLength uint64, extensive bool, + lowerBoundPtr *int64, upperBoundPtr *int64) (err error) { + var lb, ub, sizeRange int64 = 0, -1, -1 + if lowerBoundPtr != nil { + lb = *lowerBoundPtr + if upperBoundPtr != nil { + ub = *upperBoundPtr + if bitsLength <= uint64(ub) { + sizeRange = ub - lb + 1 + } else if !extensive { + err = fmt.Errorf("bitString Length is over upperbound") + return + } + if extensive { + perTrace(2, "Putting size Extension Value") + if sizeRange == -1 { + if errTmp := pd.putBitsValue(1, 1); errTmp != nil { + log.Printf("putBitsValue(1, 1) error: %v", errTmp) + } + lb = 0 + } else { + if errTmp := pd.putBitsValue(0, 1); errTmp != nil { + log.Printf("putBitsValue(0, 1) error: %v", errTmp) + } + } + } + + } + } + + if ub > 65535 { + sizeRange = -1 + } + sizes := (bitsLength + 7) >> 3 + shift := (8 - bitsLength&0x7) + if shift != 8 { + bytes[sizes-1] &= (0xff << shift) + } + + if sizeRange == 1 { + if bitsLength != uint64(ub) { + err = fmt.Errorf("bitString Length(%d) is not match fix-sized : %d", bitsLength, ub) + } + perTrace(2, fmt.Sprintf("Encoding BIT STRING size %d", ub)) + if sizes > 2 { + pd.appendAlignBits() + pd.bytes = append(pd.bytes, bytes...) + pd.bitsOffset = uint(ub & 0x7) + perTrace(1, perRawBitLog(bitsLength, len(pd.bytes), pd.bitsOffset, bytes)) + } else { + err = pd.putBitString(bytes, uint(bitsLength)) + } + perTrace(2, fmt.Sprintf("Encoded BIT STRING (length = %d): 0x%0x", bitsLength, bytes)) + return + } + rawLength := bitsLength - uint64(lb) + + var byteOffset, partOfRawLength uint64 + for { + if rawLength > 65536 { + partOfRawLength = 65536 + } else if rawLength >= 16384 { + partOfRawLength = rawLength & 0xc000 + } else { + partOfRawLength = rawLength + } + if err = pd.appendLength(sizeRange, partOfRawLength); err != nil { + return + } + partOfRawLength += uint64(lb) + sizes := (partOfRawLength + 7) >> 3 + perTrace(2, fmt.Sprintf("Encoding BIT STRING size %d", partOfRawLength)) + if partOfRawLength == 0 { + return + } + pd.appendAlignBits() + pd.bytes = append(pd.bytes, bytes[byteOffset:byteOffset+sizes]...) + perTrace(1, perRawBitLog(partOfRawLength, len(pd.bytes), pd.bitsOffset, bytes)) + perTrace(2, fmt.Sprintf("Encoded BIT STRING (length = %d): 0x%0x", partOfRawLength, + bytes[byteOffset:byteOffset+sizes])) + rawLength -= (partOfRawLength - uint64(lb)) + if rawLength > 0 { + byteOffset += sizes + } else { + pd.bitsOffset += uint(partOfRawLength & 0x7) + // pd.appendAlignBits() + break + } + } + return err + +} + +func (pd *perRawBitData) appendOctetString(bytes []byte, extensive bool, lowerBoundPtr *int64, + upperBoundPtr *int64) error { + byteLen := uint64(len(bytes)) + var lb, ub, sizeRange int64 = 0, -1, -1 + if lowerBoundPtr != nil { + lb = *lowerBoundPtr + if upperBoundPtr != nil { + ub = *upperBoundPtr + if byteLen <= uint64(ub) { + sizeRange = ub - lb + 1 + } else if !extensive { + err := fmt.Errorf("OctetString Length is over upperbound") + return err + } + if extensive { + perTrace(2, "Putting size Extension Value") + if sizeRange == -1 { + if errTmp := pd.putBitsValue(1, 1); errTmp != nil { + log.Printf("putBitsValue(1, 1) err: %v", errTmp) + } + lb = 0 + } else { + if errTmp := pd.putBitsValue(0, 1); errTmp != nil { + log.Printf("putBitsValue(0, 1) err: %v", errTmp) + } + } + } + + } + } + + if ub > 65535 { + sizeRange = -1 + } + + if sizeRange == 1 { + if byteLen != uint64(ub) { + err := fmt.Errorf("OctetString Length(%d) is not match fix-sized : %d", byteLen, ub) + return err + } + perTrace(2, fmt.Sprintf("Encoding OCTET STRING size %d", ub)) + if byteLen > 2 { + pd.appendAlignBits() + pd.bytes = append(pd.bytes, bytes...) + perTrace(1, perRawBitLog(byteLen*8, len(pd.bytes), 0, bytes)) + } else { + err := pd.putBitString(bytes, uint(byteLen*8)) + return err + } + perTrace(2, fmt.Sprintf("Encoded OCTET STRING (length = %d): 0x%0x", byteLen, bytes)) + return nil + } + rawLength := byteLen - uint64(lb) + + var byteOffset, partOfRawLength uint64 + for { + if rawLength > 65536 { + partOfRawLength = 65536 + } else if rawLength >= 16384 { + partOfRawLength = rawLength & 0xc000 + } else { + partOfRawLength = rawLength + } + if err := pd.appendLength(sizeRange, partOfRawLength); err != nil { + return err + } + partOfRawLength += uint64(lb) + perTrace(2, fmt.Sprintf("Encoding OCTET STRING size %d", partOfRawLength)) + if partOfRawLength == 0 { + return nil + } + pd.appendAlignBits() + pd.bytes = append(pd.bytes, bytes[byteOffset:byteOffset+partOfRawLength]...) + perTrace(1, perRawBitLog(partOfRawLength*8, len(pd.bytes), pd.bitsOffset, bytes)) + perTrace(2, fmt.Sprintf("Encoded OCTET STRING (length = %d): 0x%0x", partOfRawLength, + bytes[byteOffset:byteOffset+partOfRawLength])) + rawLength -= (partOfRawLength - uint64(lb)) + if rawLength > 0 { + byteOffset += partOfRawLength + } else { + // pd.appendAlignBits() + break + } + } + return nil + +} + +func (pd *perRawBitData) appendBool(value bool) (err error) { + perTrace(3, fmt.Sprintf("Encoding BOOLEAN Value %t", value)) + if value { + err = pd.putBitsValue(1, 1) + perTrace(2, "Encoded BOOLEAN Value : 0x1") + } else { + err = pd.putBitsValue(0, 1) + perTrace(2, "Encoded BOOLEAN Value : 0x0") + } + return +} + +func (pd *perRawBitData) appendInteger(value int64, extensive bool, lowerBoundPtr *int64, upperBoundPtr *int64) error { + var lb, valueRange int64 = 0, 0 + if lowerBoundPtr != nil { + lb = *lowerBoundPtr + if value < lb { + return fmt.Errorf("INTEGER value is smaller than lowerbound") + } + if upperBoundPtr != nil { + ub := *upperBoundPtr + if value <= ub { + valueRange = ub - lb + 1 + } else if !extensive { + return fmt.Errorf("INTEGER value is larger than upperbound") + } + if extensive { + perTrace(2, "Putting value Extension bit") + if valueRange == 0 { + perTrace(3, "Encoding INTEGER with Unconstraint Value") + valueRange = -1 + if errTmp := pd.putBitsValue(1, 1); errTmp != nil { + fmt.Printf("pd.putBitsValue(1, 1) error: %v", errTmp) + } + } else { + perTrace(3, fmt.Sprintf("Encoding INTEGER with Value Range(%d..%d)", lb, ub)) + if errTmp := pd.putBitsValue(0, 1); errTmp != nil { + fmt.Printf("pd.putBitsValue(0, 1) error: %v", errTmp) + } + } + } + + } else { + perTrace(3, fmt.Sprintf("Encoding INTEGER with Semi-Constraint Range(%d..)", lb)) + } + } else { + perTrace(3, "Encoding INTEGER with Unconstraint Value") + valueRange = -1 + } + + unsignedValue := uint64(value) + var rawLength uint + if valueRange == 1 { + perTrace(2, "Value of INTEGER is fixed") + + return nil + } + if value < 0 { + y := value >> 63 + unsignedValue = uint64(((value ^ y) - y)) - 1 + } + if valueRange <= 0 { + unsignedValue >>= 7 + } else if valueRange <= 65536 { + return pd.appendConstraintValue(valueRange, uint64(value-lb)) + } else { + unsignedValue >>= 8 + } + for rawLength = 1; rawLength <= 127; rawLength++ { + if unsignedValue == 0 { + break + } + unsignedValue >>= 8 + } + // putting length + if valueRange <= 0 { + // semi-constraint or unconstraint + pd.appendAlignBits() + pd.bytes = append(pd.bytes, byte(rawLength)) + perTrace(2, fmt.Sprintf("Encoding INTEGER Length %d in one byte", rawLength)) + + perTrace(1, perRawBitLog(8, len(pd.bytes), pd.bitsOffset, uint64(rawLength))) + } else { + // valueRange > 65536 + var byteLen uint + unsignedValueRange := uint64(valueRange - 1) + for byteLen = 1; byteLen <= 127; byteLen++ { + unsignedValueRange >>= 8 + if unsignedValueRange <= 1 { + break + } + } + var i, upper uint + // 1 ~ 8 bits + for i = 1; i <= 8; i++ { + upper = 1 << i + if upper >= byteLen { + break + } + } + perTrace(2, fmt.Sprintf("Encoding INTEGER Length %d-1 in %d bits", rawLength, i)) + if err := pd.putBitsValue(uint64(rawLength-1), i); err != nil { + return err + } + } + perTrace(2, fmt.Sprintf("Encoding INTEGER %d with %d bytes", value, rawLength)) + + rawLength *= 8 + pd.appendAlignBits() + + if valueRange < 0 { + mask := int64(1< ub { + if extensive { + return fmt.Errorf("Unsupport the extensive value of ENUMERATED") + } else { + return fmt.Errorf("ENUMERATED value is larger than upperbound") + } + } else if signedValue < lb { + return fmt.Errorf("ENUMERATED value is smaller than lowerbound") + } + if extensive { + if err := pd.putBitsValue(0, 1); err != nil { + return err + } + } + + valueRange := ub - lb + 1 + perTrace(2, fmt.Sprintf("Encoding ENUMERATED Value : %d with Value Range(%d..%d)", value, lb, ub)) + if valueRange > 1 { + return pd.appendConstraintValue(valueRange, value) + } + return nil + +} + +func (pd *perRawBitData) parseSequenceOf(v reflect.Value, params fieldParameters) error { + var lb, ub, sizeRange int64 = 0, -1, -1 + numElements := int64(v.Len()) + if params.sizeLowerBound != nil && *params.sizeLowerBound < 65536 { + lb = *params.sizeLowerBound + } + if params.sizeUpperBound != nil && *params.sizeUpperBound < 65536 { + ub = *params.sizeUpperBound + if params.sizeExtensible { + if numElements > ub { + if err := pd.putBitsValue(1, 1); err != nil { + return err + } + } else { + if err := pd.putBitsValue(0, 1); err != nil { + return err + } + sizeRange = ub - lb + 1 + } + } else if numElements > ub { + return fmt.Errorf("SEQUENCE OF Size is larger than upperbound") + } else { + sizeRange = ub - lb + 1 + } + } else { + sizeRange = -1 + } + + if numElements < lb { + return fmt.Errorf("SEQUENCE OF Size is lower than lowerbound") + } else if sizeRange == 1 { + perTrace(3, fmt.Sprintf("Encoding Length of \"SEQUENCE OF\" with fix-size %d", ub)) + if numElements != ub { + return fmt.Errorf("Encoding Length %d != fix-size %d", numElements, ub) + } + } else if sizeRange > 0 { + perTrace(3, fmt.Sprintf("Encoding Length(%d) of \"SEQUENCE OF\" with Size Range(%d..%d)", numElements, lb, ub)) + if err := pd.appendConstraintValue(sizeRange, uint64(numElements-lb)); err != nil { + return err + } + } else { + perTrace(3, fmt.Sprintf("Encoding Length(%d) of \"SEQUENCE OF\" with Semi-Constraint Range(%d..)", numElements, lb)) + pd.appendAlignBits() + pd.bytes = append(pd.bytes, byte(numElements&0xff)) + perTrace(1, perRawBitLog(8, len(pd.bytes), pd.bitsOffset, uint64(numElements))) + } + perTrace(2, fmt.Sprintf("Encoding \"SEQUENCE OF\" struct %s with len(%d)", v.Type().Elem().Name(), numElements)) + params.sizeExtensible = false + params.sizeUpperBound = nil + params.sizeLowerBound = nil + for i := 0; i < v.Len(); i++ { + if err := pd.makeField(v.Index(i), params); err != nil { + return err + } + } + return nil +} + +func (pd *perRawBitData) appendChoiceIndex(present int, extensive bool, upperBoundPtr *int64) error { + var ub int64 + rawChoice := present - 1 + if upperBoundPtr == nil { + return fmt.Errorf("The upper bound of CHIOCE is missing") + } else if ub = *upperBoundPtr; ub < 0 { + return fmt.Errorf("The upper bound of CHIOCE is negative") + } else if extensive && rawChoice > int(ub) { + return fmt.Errorf("Unsupport value of CHOICE type is in Extensed") + } + perTrace(2, fmt.Sprintf("Encoding Present index of CHOICE %d - 1", present)) + if err := pd.appendConstraintValue(ub+1, uint64(rawChoice)); err != nil { + return err + } + return nil +} + +func (pd *perRawBitData) appendOpenType(v reflect.Value, params fieldParameters) error { + + pdOpenType := &perRawBitData{[]byte(""), 0} + perTrace(2, fmt.Sprintf("Encoding OpenType %s to temp RawData", v.Type().String())) + if err := pdOpenType.makeField(v, params); err != nil { + return err + } + openTypeBytes := pdOpenType.bytes + rawLength := uint64(len(pdOpenType.bytes)) + perTrace(2, fmt.Sprintf("Encoding OpenType %s RawData : 0x%0x(%d bytes)", v.Type().String(), pdOpenType.bytes, + rawLength)) + + var byteOffset, partOfRawLength uint64 + for { + if rawLength > 65536 { + partOfRawLength = 65536 + } else if rawLength >= 16384 { + partOfRawLength = rawLength & 0xc000 + } else { + partOfRawLength = rawLength + } + if err := pd.appendLength(-1, partOfRawLength); err != nil { + return err + } + perTrace(2, fmt.Sprintf("Encoding Part of OpenType RawData size %d", partOfRawLength)) + if partOfRawLength == 0 { + return nil + } + pd.appendAlignBits() + pd.bytes = append(pd.bytes, openTypeBytes[byteOffset:byteOffset+partOfRawLength]...) + perTrace(1, perRawBitLog(partOfRawLength*8, len(pd.bytes), pd.bitsOffset, openTypeBytes)) + perTrace(2, fmt.Sprintf("Encoded OpenType RawData (length = %d): 0x%0x", partOfRawLength, + openTypeBytes[byteOffset:byteOffset+partOfRawLength])) + rawLength -= partOfRawLength + if rawLength > 0 { + byteOffset += partOfRawLength + } else { + pd.appendAlignBits() + break + } + } + + perTrace(2, fmt.Sprintf("Encoded OpenType %s", v.Type().String())) + return nil +} +func (pd *perRawBitData) makeField(v reflect.Value, params fieldParameters) error { + if !v.IsValid() { + return fmt.Errorf("aper: cannot marshal nil value") + } + // If the field is an interface{} then recurse into it. + if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 { + return pd.makeField(v.Elem(), params) + } + if v.Kind() == reflect.Ptr { + return pd.makeField(v.Elem(), params) + } + fieldType := v.Type() + + // We deal with the structures defined in this package first. + switch fieldType { + case BitStringType: + err := pd.appendBitString(v.Field(0).Bytes(), v.Field(1).Uint(), params.sizeExtensible, params.sizeLowerBound, + params.sizeUpperBound) + return err + case ObjectIdentifierType: + err := fmt.Errorf("Unsupport ObjectIdenfier type") + return err + case OctetStringType: + err := pd.appendOctetString(v.Bytes(), params.sizeExtensible, params.sizeLowerBound, params.sizeUpperBound) + return err + case EnumeratedType: + err := pd.appendEnumerated(v.Uint(), params.valueExtensible, params.valueLowerBound, params.valueUpperBound) + return err + } + switch val := v; val.Kind() { + case reflect.Bool: + err := pd.appendBool(v.Bool()) + return err + case reflect.Int, reflect.Int32, reflect.Int64: + err := pd.appendInteger(v.Int(), params.valueExtensible, params.valueLowerBound, params.valueUpperBound) + return err + + case reflect.Struct: + + structType := fieldType + var structParams []fieldParameters + var optionalCount uint + var optionalPresents uint64 + var sequenceType bool + // struct extensive TODO: support extensed type + if params.valueExtensible { + perTrace(2, fmt.Sprintf("Encoding Value Extensive Bit : %t", false)) + if err := pd.putBitsValue(0, 1); err != nil { + return err + } + } + sequenceType = (structType.NumField() <= 0 || structType.Field(0).Name != "Present") + // pass tag for optional + for i := 0; i < structType.NumField(); i++ { + if structType.Field(i).PkgPath != "" { + return fmt.Errorf("struct contains unexported fields : " + structType.Field(i).PkgPath) + } + tempParams := parseFieldParameters(structType.Field(i).Tag.Get("aper")) + if sequenceType { + + // for optional flag + if tempParams.optional { + optionalCount++ + optionalPresents <<= 1 + if !v.Field(i).IsNil() { + optionalPresents++ + } + } else if v.Field(i).Type().Kind() == reflect.Ptr && v.Field(i).IsNil() { + return fmt.Errorf("nil element in SEQUENCE type") + } + } + + structParams = append(structParams, tempParams) + } + if optionalCount > 0 { + perTrace(2, fmt.Sprintf("putting optional(%d), optionalPresents is %0b", optionalCount, optionalPresents)) + if err := pd.putBitsValue(optionalPresents, optionalCount); err != nil { + return err + } + } + + // CHOICE or OpenType + if !sequenceType { + present := int(v.Field(0).Int()) + if present == 0 { + return fmt.Errorf("CHOICE or OpenType present is 0(present's field number)") + } else if present >= structType.NumField() { + return fmt.Errorf("Present is bigger than number of struct field") + } else if params.openType { + if params.referenceFieldValue == nil { + return fmt.Errorf("OpenType reference value is empty") + } + refValue := *params.referenceFieldValue + + if structParams[present].referenceFieldValue == nil || *structParams[present].referenceFieldValue != refValue { + return fmt.Errorf("reference value and present reference value is not match") + } + perTrace(2, fmt.Sprintf("Encoding Present index of OpenType is %d ", present)) + if err := pd.appendOpenType(val.Field(present), structParams[present]); err != nil { + return err + } + } else { + if err := pd.appendChoiceIndex(present, params.valueExtensible, params.valueUpperBound); err != nil { + return err + } + if err := pd.makeField(val.Field(present), structParams[present]); err != nil { + return err + } + } + return nil + + } + + for i := 0; i < structType.NumField(); i++ { + // optional + if structParams[i].optional && optionalCount > 0 { + optionalCount-- + if optionalPresents&(1<