├── setup
├── dist
├── rsa.private
├── generate.sh
├── secureKey
└── setup_storage_file.go
├── app
└── token
│ ├── dist
│ └── token_test.go
├── restful
├── libsecurity
│ ├── dist
│ └── config.json
├── accounts-restful
│ ├── dist
│ └── am_command.go
├── libsecurity-restful
│ ├── libsecurity_command.go
│ └── libsecurity_test.go
├── password-restful
│ ├── pwd_command.go
│ ├── pwd_restful.go
│ └── pwd_restful_test.go
├── otp-restful
│ ├── otp_command.go
│ └── otp_restful.go
├── storage-restful
│ ├── secureStorage_command.go
│ ├── secureStorage_restful.go
│ └── secureStorage_restful_test.go
├── ocra-restful
│ └── ocra_command.go
├── acl-restful
│ └── acl_command.go
└── common-restful
│ └── common_restful.go
├── .gitignore
├── .gitattributes
├── swagger-dist
├── images
│ ├── conf.png
│ ├── logo.jpg
│ ├── iot_logo.png
│ ├── throbber.gif
│ ├── logo_small.png
│ ├── explorer_icons.png
│ └── forewind_fav_icon.png
├── fonts
│ ├── droid-sans-v6-latin-700.eot
│ ├── droid-sans-v6-latin-700.ttf
│ ├── droid-sans-v6-latin-700.woff
│ ├── droid-sans-v6-latin-700.woff2
│ ├── droid-sans-v6-latin-regular.eot
│ ├── droid-sans-v6-latin-regular.ttf
│ ├── droid-sans-v6-latin-regular.woff
│ └── droid-sans-v6-latin-regular.woff2
├── lib
│ ├── jquery.slideto.min.js
│ ├── jquery.wiggle.min.js
│ ├── jquery.ba-bbq.min.js
│ ├── highlight.7.3.pack.js
│ └── shred
│ │ └── content.js
├── o2c.html
├── security.json
├── secureKey
├── server.csr
├── server.crt
├── data.txt
├── css
│ ├── typography.css
│ └── reset.css
├── server.key
├── forewind
│ └── app
│ │ └── v1
│ │ ├── libsecurity
│ │ ├── securestorage
│ │ └── password
├── key.private
└── index.html
├── CONTRIBUTING.md
├── logger
├── logger_example_test.go
└── logger.go
├── entity
└── entityManagmentUtils_test.go
├── otp
├── gauth_js.go
├── otpTotp_test.go
├── otp_examples_test.go
├── otpExternal_test.go
├── otp_test.go
└── otpHotp_test.go
├── storage
├── storage_example_test.go
└── secureStorage_test.go
├── defs
├── libsecurityCommonForTest.go
└── libsecurityCommon.go
├── acl
├── aclEntry.go
├── acl_example_test.go
└── aclEntry_test.go
├── salt
├── salt_examples_test.go
├── salt.go
└── salt_test.go
├── password
└── password_example_test.go
└── accounts
└── accountManagement_test.go
/setup/dist:
--------------------------------------------------------------------------------
1 | ../swagger-dist
--------------------------------------------------------------------------------
/app/token/dist:
--------------------------------------------------------------------------------
1 | ../../swagger-dist
--------------------------------------------------------------------------------
/restful/libsecurity/dist:
--------------------------------------------------------------------------------
1 | ../../swagger-dist
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.txt
2 | *.sh
3 | tmp
4 | *.out
5 |
--------------------------------------------------------------------------------
/restful/accounts-restful/dist:
--------------------------------------------------------------------------------
1 | ../../swagger-dist
--------------------------------------------------------------------------------
/setup/rsa.private:
--------------------------------------------------------------------------------
1 | ../swagger-dist/rsa.private
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | .rb linguist-language=Go
2 | swagger-dist/* linguist-vendored
3 |
--------------------------------------------------------------------------------
/setup/generate.sh:
--------------------------------------------------------------------------------
1 | go run setup_storage_file.go -password="Im the root12#^"
2 | #cp data.txt dist
3 |
--------------------------------------------------------------------------------
/swagger-dist/images/conf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/images/conf.png
--------------------------------------------------------------------------------
/swagger-dist/images/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/images/logo.jpg
--------------------------------------------------------------------------------
/swagger-dist/images/iot_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/images/iot_logo.png
--------------------------------------------------------------------------------
/swagger-dist/images/throbber.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/images/throbber.gif
--------------------------------------------------------------------------------
/swagger-dist/images/logo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/images/logo_small.png
--------------------------------------------------------------------------------
/swagger-dist/images/explorer_icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/images/explorer_icons.png
--------------------------------------------------------------------------------
/swagger-dist/images/forewind_fav_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/images/forewind_fav_icon.png
--------------------------------------------------------------------------------
/swagger-dist/fonts/droid-sans-v6-latin-700.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/fonts/droid-sans-v6-latin-700.eot
--------------------------------------------------------------------------------
/swagger-dist/fonts/droid-sans-v6-latin-700.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/fonts/droid-sans-v6-latin-700.ttf
--------------------------------------------------------------------------------
/swagger-dist/fonts/droid-sans-v6-latin-700.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/fonts/droid-sans-v6-latin-700.woff
--------------------------------------------------------------------------------
/swagger-dist/fonts/droid-sans-v6-latin-700.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/fonts/droid-sans-v6-latin-700.woff2
--------------------------------------------------------------------------------
/swagger-dist/fonts/droid-sans-v6-latin-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/fonts/droid-sans-v6-latin-regular.eot
--------------------------------------------------------------------------------
/swagger-dist/fonts/droid-sans-v6-latin-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/fonts/droid-sans-v6-latin-regular.ttf
--------------------------------------------------------------------------------
/swagger-dist/fonts/droid-sans-v6-latin-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/fonts/droid-sans-v6-latin-regular.woff
--------------------------------------------------------------------------------
/swagger-dist/fonts/droid-sans-v6-latin-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibm-security-innovation/libsecurity-go/HEAD/swagger-dist/fonts/droid-sans-v6-latin-regular.woff2
--------------------------------------------------------------------------------
/restful/libsecurity/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "accountManager": "full",
3 | "otp": "basic",
4 | "acl": "basic",
5 | "appAcl": "none",
6 | "um": "basic",
7 | "ocra": "basic",
8 | "password": "basic",
9 | "secureStorage": "basic"
10 | }
11 |
--------------------------------------------------------------------------------
/swagger-dist/lib/jquery.slideto.min.js:
--------------------------------------------------------------------------------
1 | (function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery);
2 |
--------------------------------------------------------------------------------
/swagger-dist/o2c.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/swagger-dist/security.json:
--------------------------------------------------------------------------------
1 | {"apiVersion":"2.02","apis":[{"description":"","path":"/../forewind/app/v1/account-manager"},{"description":"","path":"/../forewind/app/v1/entity"},{"description":"","path":"/../forewind/app/v1/acl"},{"description":"","path":"/../forewind/app/v1/otp"},{"description":"","path":"/../forewind/app/v1/ocra"},{"description":"","path":"/../forewind/app/v1/password"},{"description":"Secure Storage","path":"/../forewind/app/v1/securestorage"},{"description":"The Security Tool","path":"/../forewind/app/v1/libsecurity"}],"info":{"description":"","title":"Libsecurity API"},"swaggerVersion":"1.2"}
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | To contribute, please submit a Pull Request. The only thing that we ask that you do
2 | is a very simple step, include a line modeled after this line:
3 | “DCO 1.1 Signed-off-by: Random J Developer <random@developer.org>"
4 | as part of your pull request comments. By doing this one simple thing you are telling
5 | the community that you wrote the code you are contributing or have the right to pass
6 | on the code that you are contributing. The
7 | DCO 1.1 referenced is the Developer Certificate of Origin that is used in the Linux Kernel community, you can find it [here](http://elinux.org/Developer_Certificate_Of_Origin "DCO 1.1").
8 |
9 | Thank you!
10 |
--------------------------------------------------------------------------------
/swagger-dist/lib/jquery.wiggle.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | jQuery Wiggle
3 | Author: WonderGroup, Jordan Thomas
4 | URL: http://labs.wondergroup.com/demos/mini-ui/index.html
5 | License: MIT (http://en.wikipedia.org/wiki/MIT_License)
6 | */
7 | jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('
').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);}
8 | if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});};
--------------------------------------------------------------------------------
/setup/secureKey:
--------------------------------------------------------------------------------
1 | MIICXAIBAAKBgQC4xn/4m0Hs2vnbVjMCmpcTAN0Zx0fhzkVJFF5TPxPX1w3gthrG
2 | zzoI5xm4CWkZauXXhViR9FRtWnI7NqesYJsvbBxdj77XS0bmRNrs8WsectTVfxSO
3 | 99BuysINhxnN5IV560R23qS3Pur1u+OfZxGUTT+hc7bgieY+GQao+/Zq8QIDAQAB
4 | AoGAGABo5SE9ygeTxyMGU0NDdot5LP0O4E0MAPctXYDnEfV0bwkYp1SJxFhIpfhK
5 | 96tEGd/PZn4nbWD/T6PE2gZegez1SFIMguBs4dRYNg5FVEl6/r2FaT+DbqGHVGdl
6 | TtSnONPis1c1AgAHG8dYZ8Ep30qRUpCFKF5cpYvecW1gIfECQQDdKLDNvder0+g1
7 | bvyxck0Wt9x+jYpz6DMPbv/CCmxcMcf+By/uV2N2I95U77Dx+KIZVe54x+Kyuzhs
8 | GOHf2RnFAkEA1eJ3KQEe/U9hQoMcl1GK4KkZlY7X3HKeZeRcr6CuL7RFTf57Dj4x
9 | 7Cs0raVSN9KYRnqM4lfihRJtIvXbry2bPQJAB2IwZO4NoUL14bLO5O23/SYuSltX
10 | X/9ElSNTjprie0F+N9DtnLt2Kp+P8K3/7lQqtUXVLFsgCm+Ntq4t/Fc3MQJAW5US
11 | 7O3b9R8uSCqX7Xsu+i+uEsoNSAmUJZD41wL4Utjf7BuRWXHsMnH1pnzpjZmRNmx3
12 | Mtvq1IwlSxj3LjVbCQJBALJdLki0BdNVBEEmfwbwaED6Ujx9LlDcY9hDfcc6D/Eo
13 | FtHYRHBulG59peDn1rT0tkl9hUsVA7EC+f8SOlJwrxo=
14 |
--------------------------------------------------------------------------------
/swagger-dist/secureKey:
--------------------------------------------------------------------------------
1 | MIICXAIBAAKBgQC4xn/4m0Hs2vnbVjMCmpcTAN0Zx0fhzkVJFF5TPxPX1w3gthrG
2 | zzoI5xm4CWkZauXXhViR9FRtWnI7NqesYJsvbBxdj77XS0bmRNrs8WsectTVfxSO
3 | 99BuysINhxnN5IV560R23qS3Pur1u+OfZxGUTT+hc7bgieY+GQao+/Zq8QIDAQAB
4 | AoGAGABo5SE9ygeTxyMGU0NDdot5LP0O4E0MAPctXYDnEfV0bwkYp1SJxFhIpfhK
5 | 96tEGd/PZn4nbWD/T6PE2gZegez1SFIMguBs4dRYNg5FVEl6/r2FaT+DbqGHVGdl
6 | TtSnONPis1c1AgAHG8dYZ8Ep30qRUpCFKF5cpYvecW1gIfECQQDdKLDNvder0+g1
7 | bvyxck0Wt9x+jYpz6DMPbv/CCmxcMcf+By/uV2N2I95U77Dx+KIZVe54x+Kyuzhs
8 | GOHf2RnFAkEA1eJ3KQEe/U9hQoMcl1GK4KkZlY7X3HKeZeRcr6CuL7RFTf57Dj4x
9 | 7Cs0raVSN9KYRnqM4lfihRJtIvXbry2bPQJAB2IwZO4NoUL14bLO5O23/SYuSltX
10 | X/9ElSNTjprie0F+N9DtnLt2Kp+P8K3/7lQqtUXVLFsgCm+Ntq4t/Fc3MQJAW5US
11 | 7O3b9R8uSCqX7Xsu+i+uEsoNSAmUJZD41wL4Utjf7BuRWXHsMnH1pnzpjZmRNmx3
12 | Mtvq1IwlSxj3LjVbCQJBALJdLki0BdNVBEEmfwbwaED6Ujx9LlDcY9hDfcc6D/Eo
13 | FtHYRHBulG59peDn1rT0tkl9hUsVA7EC+f8SOlJwrxo=
14 |
--------------------------------------------------------------------------------
/swagger-dist/server.csr:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE REQUEST-----
2 | MIIChjCCAW4CAQAwQTELMAkGA1UEBhMCSUwxFDASBgNVBAoMC2R1bW15LmxvY2Fs
3 | MRwwGgYDVQQDDBNpby10ZXN0LmR1bW15LmxvY2FsMIIBIjANBgkqhkiG9w0BAQEF
4 | AAOCAQ8AMIIBCgKCAQEAzGAOB+BJwqLXF8nm/iKfdvuc2QOgmKw3qllNkQEdWbTa
5 | 7Wn/5opTkXbS7sZwr59JTfeyB9LKNufjDRBbrUCTdpcVqN1D4TgDywyl/NR0AhV+
6 | r8NuPnogR3PZU83lvMi1ErQ0w75N3K0FLaOO9KMhkfOffOpn+SS7KOYSqbN7xaNj
7 | gkP+9XM4H5nEtyyhaeQy4oWSscSCu9lx1dG3YlewyL5+66gwkv4HJWNnsAGPEDAB
8 | VhVWh+uYbV4EhH21If/QYfZFVRtgWEva91x6xePXvIClk+wKhNcgMTpBt2F5m70m
9 | TUH9V1nhMnWdUitWd5Gvb1viKg4AqqhL8nEDto+ErwIDAQABoAAwDQYJKoZIhvcN
10 | AQELBQADggEBADL6+V712HZ5rTds0pSouYZOfJTR6a2azDZQtMr2inhYOfE2dUjL
11 | k7OnGUeoFBkfh7fq3g2jDCqqI9m29mSORb/3KhSkavA5j6/nfPAnmn8rdNqRoCec
12 | V2TPL+36RUWeF2AZZkFv6pS9fkiAMlIs4E9kwfEXljnjO6yuvR1Wc+lgitvWfJKU
13 | qNuK54d65goEZJvxnnVyaCfkwibdOm0PiMRzG2S7kAul66fYlHeAl22AdczsWWDQ
14 | TS9W9D0x1XvgZqeiCMg53bz0LmxwUrkK+Zrt+GmzHpJJSxj24FzPUJec4IwN0moT
15 | ex29eWZjIV5er2Tf49ykQGRrySsYOUnpVOQ=
16 | -----END CERTIFICATE REQUEST-----
17 |
--------------------------------------------------------------------------------
/logger/logger_example_test.go:
--------------------------------------------------------------------------------
1 | package logger_test
2 |
3 | import (
4 | "io"
5 | "io/ioutil"
6 | "log"
7 | "os"
8 |
9 | logger "github.com/ibm-security-innovation/libsecurity-go/logger"
10 | )
11 |
12 | // This example show the following:
13 | // - Discard Trace messages
14 | // - Write Info messages to stdout
15 | // - Write Warning to stdout and log file
16 | // - Write Error to stderr and log file
17 | func Example_logger() {
18 | fileName := "log-file.txt"
19 | os.Remove(fileName)
20 | file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
21 | if err != nil {
22 | log.Fatalln("Failed to open log file", fileName, ":", err)
23 | }
24 |
25 | multiW := io.MultiWriter(file, os.Stdout)
26 | multiE := io.MultiWriter(file, os.Stderr)
27 | logger.Init(ioutil.Discard, os.Stdout, multiW, multiE)
28 |
29 | logger.Trace.Println("Example: I have something standard to say")
30 | logger.Info.Println("Example: Special Information")
31 | logger.Warning.Println("Example: There is something you need to know about")
32 | logger.Error.Println("Example: Something has failed")
33 | }
34 |
--------------------------------------------------------------------------------
/swagger-dist/server.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIC/jCCAeYCCQDjmfl0P3G5uDANBgkqhkiG9w0BAQsFADBBMQswCQYDVQQGEwJJ
3 | TDEUMBIGA1UECgwLZHVtbXkubG9jYWwxHDAaBgNVBAMME2lvLXRlc3QuZHVtbXku
4 | bG9jYWwwHhcNMTYwMTA0MDkyMTE5WhcNMjYwMTAxMDkyMTE5WjBBMQswCQYDVQQG
5 | EwJJTDEUMBIGA1UECgwLZHVtbXkubG9jYWwxHDAaBgNVBAMME2lvLXRlc3QuZHVt
6 | bXkubG9jYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMYA4H4EnC
7 | otcXyeb+Ip92+5zZA6CYrDeqWU2RAR1ZtNrtaf/milORdtLuxnCvn0lN97IH0so2
8 | 5+MNEFutQJN2lxWo3UPhOAPLDKX81HQCFX6vw24+eiBHc9lTzeW8yLUStDTDvk3c
9 | rQUto470oyGR85986mf5JLso5hKps3vFo2OCQ/71czgfmcS3LKFp5DLihZKxxIK7
10 | 2XHV0bdiV7DIvn7rqDCS/gclY2ewAY8QMAFWFVaH65htXgSEfbUh/9Bh9kVVG2BY
11 | S9r3XHrF49e8gKWT7AqE1yAxOkG3YXmbvSZNQf1XWeEydZ1SK1Z3ka9vW+IqDgCq
12 | qEvycQO2j4SvAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACkY0eJKdzgRF4EjTZ9P
13 | G9xb3CHuvsrg/QsLVxbQcwIyKBEKVXMNSH6EK8SUIn1UDeDepggwMYq9u689H4/V
14 | dTY6OirfoQh8UtKe1GQCY2gRl8z6hoMsD2C/0Piy79x7mKzTtgKqakAV2dPehfk8
15 | udanV8uhfcF/DwXO5qm+0UdLDJCsP2f3Kw4fcOo9IDHPjcIv1b7dWAXSaSkBrhZa
16 | sWh/YIZG4OHW+JFFx1tOtl3tX6MdrklT04IsZ0mL5iyzTvVIBHLc5ml/4IpOppkR
17 | wZ09M2+TB7fTcknQC1AMBVSbHelRwFN2orum2JYZrXGKV2RLEf5DtqHVulQnAS5F
18 | 8N8=
19 | -----END CERTIFICATE-----
20 |
--------------------------------------------------------------------------------
/swagger-dist/data.txt:
--------------------------------------------------------------------------------
1 | {"Salt":"/XJQj3+3LDQ=","Sign":"czJ9qKW4FCR1kvHQLFew1W5I4ZuYhcSmv8BbIAFj/yg=","Data":{"Dh2J2RRr7Y63PA9I+/a9oyPBmvj6DUgqzbxt8ZdvH6Y=":"NVnWHfLEmoJnUagrKWABZA==","TlZuV0hmTEVtb0puVWFncq58m/tTjdE5k5/PGGqOsaE=":"JIz1ycPTUU3oPA+WdoNYU2CAeSxQAkqCLTSS12SYYmUdxCc6SuKhscaYC+QiKnL1LBO6BzTW795INpAxrpq/6UYHzZApGVp+MIXzCZypkUwUjWwjcenS34F8EjJ71pRxm/VXR+MsfOp1AEXnOlskkaIPDcIiCm2uUiL5MyJM55C5z32hM6O+ScysSpxnm7fWBAEwDQqoyY3wHvLWyQztXW3hGbTETOIhsoXTTtc5kTSgKolqJtNhm7y662sMieNfFfGBKmdSlyJvwrV7sLYpECK1LnEJBjB4pFnL5L6BcMiljxQl2zhb5M8Ck5eQmJRrnAF2A/BUJ1lpOcpyt5D8dQ3E0FwYmrIE9uvgD4MqhI3UaXbMcezvWtfm4c7aq5Ik+hImMHXLhLhSGfIOXPgkJdtURADAm6PBzy0R9nA9GfvGOiUq0tnGnl8XBHMvcTVTzyuAvwwHlEWWHbGs1bwqNw==","WHJSNitKd2kwdzFSTHhJTr8OoBk+UbwmJB1am0NkyEvoVanCrpDRi3jI1AaHLF6o":"TpO9nP07S8Nrr3hjAQOPj0T964fetcdfr2x9Dd8ItoqFAo/+9e2zzD83piqLrvj4VvqtAS8Ei2eF1Z6b7dK/Ig==","ZmVLTWlWaGJ0WEo2ZzdVeBLbmlar2rXICPAa93HP1pZhhV7hcZIdSWxVVkH66QE3":"SzLjPVrUY48AESK2tcyL9JQgrE37wggcVtrSyVs6Kr+kgN5QAYYEeEQUjgAWcvNjE3/8MrUQI/fi0sRhFD7aQtaWdXvJvzHM9WtxP3aSxXzuAKAPm0a3HcH8v5xXw+/invn4EuyH3Rnt/9rKcWWdzURd6+KFmfXCe7W3vaou0pi0LKbKMrHOeEmvbK4VLJNYEI19rDt0CdXlFrFFiayusfzmkpIgAZ+I5OIOqol17FNLZ+osEoyVaY9FckctRt5WyhaDs3OFDHe+B73g+SabZsPRG7DWyFnZswIps8kypTrkpWI0Wg8kTBr/HqgGrL5k2zsD+3nr80C/qLwGk5ZW3rFZoDc1CB8SImBKfySZlTwy9kHtuT/E60GmPIVkLC6mPFlTPl+jEmK0IW4KZVAQ/hN/3IzHgyk5458FXb6M21zQus8VdMMqv8K2fSO2Xrx5fJ/ngxNHrw+sRGqO3QQLguD4ieqNHo6vJcWB75bql8PkCk2K5ovzUPgJa3wK1gwisN/RViy1LUEINg39xxMRwg==","gyEMyn7EU4gbsCqzmrDAvLhJ/nrjRIRuyc7NtgYVAzQ=":"XrR6+Jwi0w1RLxINZvnmqg==","oIej+hRuRXpy4m282C3iAbWjL0MmSsSkTXTAtGCFhVs=":"feKMiVhbtXJ6g7UxFlvVXg=="},"Version":"V 1.2"}
--------------------------------------------------------------------------------
/swagger-dist/css/typography.css:
--------------------------------------------------------------------------------
1 | /* droid-sans-regular - latin */
2 | @font-face {
3 | font-family: 'Droid Sans';
4 | font-style: normal;
5 | font-weight: 400;
6 | src: url('../fonts/droid-sans-v6-latin-regular.eot'); /* IE9 Compat Modes */
7 | src: local('Droid Sans'), local('DroidSans'),
8 | url('../fonts/droid-sans-v6-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
9 | url('../fonts/droid-sans-v6-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
10 | url('../fonts/droid-sans-v6-latin-regular.woff') format('woff'), /* Modern Browsers */
11 | url('../fonts/droid-sans-v6-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
12 | url('../fonts/droid-sans-v6-latin-regular.svg#DroidSans') format('svg'); /* Legacy iOS */
13 | }
14 | /* droid-sans-700 - latin */
15 | @font-face {
16 | font-family: 'Droid Sans';
17 | font-style: normal;
18 | font-weight: 700;
19 | src: url('../fonts/droid-sans-v6-latin-700.eot'); /* IE9 Compat Modes */
20 | src: local('Droid Sans Bold'), local('DroidSans-Bold'),
21 | url('../fonts/droid-sans-v6-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
22 | url('../fonts/droid-sans-v6-latin-700.woff2') format('woff2'), /* Super Modern Browsers */
23 | url('../fonts/droid-sans-v6-latin-700.woff') format('woff'), /* Modern Browsers */
24 | url('../fonts/droid-sans-v6-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */
25 | url('../fonts/droid-sans-v6-latin-700.svg#DroidSans') format('svg'); /* Legacy iOS */
26 | }
27 |
--------------------------------------------------------------------------------
/entity/entityManagmentUtils_test.go:
--------------------------------------------------------------------------------
1 | package entityManagement
2 |
3 | import (
4 | "fmt"
5 |
6 | am "github.com/ibm-security-innovation/libsecurity-go/accounts"
7 | // "github.com/ibm-security-innovation/libsecurity-go/acl"
8 | defs "github.com/ibm-security-innovation/libsecurity-go/defs"
9 | "github.com/ibm-security-innovation/libsecurity-go/ocra"
10 | "github.com/ibm-security-innovation/libsecurity-go/otp"
11 | "github.com/ibm-security-innovation/libsecurity-go/password"
12 | )
13 |
14 | func GenerateUserData(el *EntityManager, usersName []string, secret []byte, salt []byte) {
15 | el.AddUser(usersName[0])
16 | el.AddResource("r"+usersName[0])
17 | amData, _ := am.NewUserAm(am.SuperUserPermission, secret, salt, false)
18 | el.AddPropertyToEntity(usersName[0], defs.AmPropertyName, amData)
19 | otpData, _ := otp.NewSimpleOtpUser(secret, false)
20 | el.AddPropertyToEntity(usersName[0], defs.OtpPropertyName, otpData)
21 | pwdData, _ := password.NewUserPwd(secret, salt, false)
22 | el.AddPropertyToEntity(usersName[0], defs.PwdPropertyName, pwdData)
23 | ocraData, _ := ocra.NewOcraUser([]byte("ABCD1234"), "OCRA-1:HOTP-SHA512-8:C-QH08-T1M-S064-PSHA256")
24 | el.AddPropertyToEntity(usersName[0], defs.OcraPropertyName, ocraData)
25 |
26 | el.AddUser(usersName[1])
27 | el.AddPropertyToEntity(usersName[1], defs.OtpPropertyName, otpData)
28 | }
29 |
30 | func GenerateGroupList(el *EntityManager, usersName []string) {
31 | for i := 0; i < 3; i++ {
32 | groupName := fmt.Sprintf("group-%d", i+1)
33 | el.AddGroup(groupName)
34 | for j := 0; j < i; j++ {
35 | el.AddUserToGroup(groupName, usersName[j])
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/otp/gauth_js.go:
--------------------------------------------------------------------------------
1 | package otp
2 |
3 | import (
4 | "crypto/hmac"
5 | "crypto/sha1"
6 | "encoding/hex"
7 | "fmt"
8 | "strconv"
9 | "strings"
10 | )
11 |
12 | // Porting of the JS implementation of Google Authenticator
13 | // presented in http://fiddle.jshell.net/russau/ch8PK/show/light/?secret=abcd
14 |
15 | func base32tohex(base32 string) string {
16 | base32chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
17 | bits := ""
18 | hex := ""
19 | base32u := strings.ToUpper(base32)
20 |
21 | for i := 0; i < len(base32); i++ {
22 | val := strings.Index(base32chars, string(base32u[i]))
23 | // fmt.Println(fmt.Sprintf("%05b", val))
24 | bits += fmt.Sprintf("%05b", val)
25 | }
26 |
27 | for i := 0; (i + 4) <= len(bits); i += 4 {
28 | chunk := bits[i : i+4]
29 | cval, _ := strconv.ParseInt(chunk, 2, 8)
30 | chex := fmt.Sprintf("%0x", cval)
31 | // fmt.Println(chunk, cval, chex)
32 | hex = hex + chex
33 | }
34 | return hex
35 | }
36 |
37 | func jsOtp(base32secret string, epoch int64) string {
38 | key := base32tohex(base32secret)
39 | time := fmt.Sprintf("%016x", epoch/30)
40 | //fmt.Println(key,epoch,time)
41 |
42 | k, _ := hex.DecodeString(key)
43 | v, _ := hex.DecodeString(time)
44 | hm := hmac.New(sha1.New, k)
45 | hm.Write(v)
46 | hmcout := hex.EncodeToString(hm.Sum(nil))
47 |
48 | offset, _ := strconv.ParseInt(hmcout[len(hmcout)-1:], 16, 64)
49 | substr := hmcout[offset*2 : offset*2+8]
50 | psubstr, _ := strconv.ParseInt(substr, 16, 64)
51 | pmask, _ := strconv.ParseInt("7fffffff", 16, 64)
52 | otpEx := fmt.Sprintf("%d", psubstr&pmask)
53 | // fmt.Println(offset,otp_ex[len(otp_ex)-6:])
54 | return otpEx[len(otpEx)-6:]
55 | }
56 |
--------------------------------------------------------------------------------
/swagger-dist/server.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEogIBAAKCAQEAzGAOB+BJwqLXF8nm/iKfdvuc2QOgmKw3qllNkQEdWbTa7Wn/
3 | 5opTkXbS7sZwr59JTfeyB9LKNufjDRBbrUCTdpcVqN1D4TgDywyl/NR0AhV+r8Nu
4 | PnogR3PZU83lvMi1ErQ0w75N3K0FLaOO9KMhkfOffOpn+SS7KOYSqbN7xaNjgkP+
5 | 9XM4H5nEtyyhaeQy4oWSscSCu9lx1dG3YlewyL5+66gwkv4HJWNnsAGPEDABVhVW
6 | h+uYbV4EhH21If/QYfZFVRtgWEva91x6xePXvIClk+wKhNcgMTpBt2F5m70mTUH9
7 | V1nhMnWdUitWd5Gvb1viKg4AqqhL8nEDto+ErwIDAQABAoIBABH/J10kL/qWslAi
8 | 9FdOnzIwB+jIO/0VtTUn9f+d1ILQvTKI0gLwDVnS19I04Tll0mOuOxwbQLlDBChj
9 | V19TP2kC9uJO0apkb1kS/Q7denWwhN5fXDbeNDd2iR2rFN7h4agK/FZ8TIjxJ3KU
10 | Y4NBVjRu+o1aattyk5SPZfdRmcWWbEfe3JJ7q2F9oh64JhVp53Xe3VTHDbgTKZoH
11 | 3Y47ER7lu173CimoF9ufCfCgD8QLBfmFVXOISZar47m9CRIvRlMm4P+yTygq+6zG
12 | L2a25VLCt9lr5cHkYApq7fvW2fAccQwHOOOM1UZlBRxbijsXOhJYDDq8GsQqb+YN
13 | 5RGLaTECgYEA9pjhdWAL2zxidORzkBodPOBhxWcW89go1YmNVquaKDeh9dqusDzk
14 | OUr887LZpCUIbRB/LH4PJ/GvbQnNhu98joWuEP9Hinij3w+gOM9BxwUnA2Z6Y1Ox
15 | 3T4wdJZSK7ItnRGGPuAmybOdxwR5jZ87EDG5/lEi/t9pEVk4icA7v5kCgYEA1CsH
16 | 72sS6G+oiPy4O8iGYPg7s+8O3INj4e8io201bBboLdTwDAnhIkhIqa3XNb+xAT1S
17 | bZq1CuazaMjzCf1xckhJ+N4hmsPs1lfiC1xs67Dqh5mPUmKwgntQDJ5AEMlLWSBl
18 | Vb4N7RPVmMKCVoCHuJbVoeThW9UKnYsUOqQyM4cCgYBZqHQwzN8Csw3JGla6CgSf
19 | YaWqtDSwK+OM4Bo3bOT7wl5smTnycfE4s38My/Y9rAlIPjs+3gZ8q27w4iOL7/8F
20 | gq0rGOXV/s72ByRmA1UTG1h38AaHnqKIolfTy+PMZ1gWR4zbx3vS0i+HcKURTNeY
21 | p7vML4vdxzTYTew0iWQv4QKBgCpEaFBKBHpJxEmGDy3ZxOuMiFhQR6Wmhz97ZGsq
22 | VnoyFg8LXMcJMNCjldY3y6fpiFm23bT4HRkO3K8DWWekFfWucQ4jMo6ieYw59c5N
23 | m2VY2WAqN2MyRfe4mTnqTNkLQPeAfK+YLS4NxX+pQE37TQ54/hZCgqLIzlSUYHNO
24 | QrIBAoGAZLqwfluSDRgUgNSk6++efb6N6hYqIUA259wF2Z+rN2Y0kqR+OTwcScTx
25 | OfN7MGUChDCkFVLvgEjHeK8jKOprk0GqQyN5s4TMNZrDOIWj+K8Vo5hewEDLwzgt
26 | zh9+nNrNwLrHgWn65JeXFe62Ptrb18OWc5XNafUBaWVqK6DXuhc=
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/swagger-dist/css/reset.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */
2 | html,
3 | body,
4 | div,
5 | span,
6 | applet,
7 | object,
8 | iframe,
9 | h1,
10 | h2,
11 | h3,
12 | h4,
13 | h5,
14 | h6,
15 | p,
16 | blockquote,
17 | pre,
18 | a,
19 | abbr,
20 | acronym,
21 | address,
22 | big,
23 | cite,
24 | code,
25 | del,
26 | dfn,
27 | em,
28 | img,
29 | ins,
30 | kbd,
31 | q,
32 | s,
33 | samp,
34 | small,
35 | strike,
36 | strong,
37 | sub,
38 | sup,
39 | tt,
40 | var,
41 | b,
42 | u,
43 | i,
44 | center,
45 | dl,
46 | dt,
47 | dd,
48 | ol,
49 | ul,
50 | li,
51 | fieldset,
52 | form,
53 | label,
54 | legend,
55 | table,
56 | caption,
57 | tbody,
58 | tfoot,
59 | thead,
60 | tr,
61 | th,
62 | td,
63 | article,
64 | aside,
65 | canvas,
66 | details,
67 | embed,
68 | figure,
69 | figcaption,
70 | footer,
71 | header,
72 | hgroup,
73 | menu,
74 | nav,
75 | output,
76 | ruby,
77 | section,
78 | summary,
79 | time,
80 | mark,
81 | audio,
82 | video {
83 | margin: 0;
84 | padding: 0;
85 | border: 0;
86 | font-size: 100%;
87 | font: inherit;
88 | vertical-align: baseline;
89 | }
90 | /* HTML5 display-role reset for older browsers */
91 | article,
92 | aside,
93 | details,
94 | figcaption,
95 | figure,
96 | footer,
97 | header,
98 | hgroup,
99 | menu,
100 | nav,
101 | section {
102 | display: block;
103 | }
104 | body {
105 | line-height: 1;
106 | }
107 | ol,
108 | ul {
109 | list-style: none;
110 | }
111 | blockquote,
112 | q {
113 | quotes: none;
114 | }
115 | blockquote:before,
116 | blockquote:after,
117 | q:before,
118 | q:after {
119 | content: '';
120 | content: none;
121 | }
122 | table {
123 | border-collapse: collapse;
124 | border-spacing: 0;
125 | }
126 |
--------------------------------------------------------------------------------
/logger/logger.go:
--------------------------------------------------------------------------------
1 | // Package logger : The logger package contains the implementation of log handling.
2 | package logger
3 |
4 | import (
5 | "io"
6 | "io/ioutil"
7 | "log"
8 | "os"
9 | )
10 |
11 | var (
12 | // Trace : write trace information to the logger
13 | Trace *log.Logger
14 | // Info : write info and trace information to the logger
15 | Info *log.Logger
16 | // Warning : write warnings, info and trace information to the logger
17 | Warning *log.Logger
18 | // Error : write errors, warnings, info and trace information to the logger
19 | Error *log.Logger
20 | )
21 |
22 | func init() {
23 | Init(ioutil.Discard, ioutil.Discard, os.Stdout, os.Stderr)
24 | }
25 |
26 | // Init : initialize the logger
27 | func Init(traceHandle io.Writer, infoHandle io.Writer, warningHandle io.Writer, errorHandle io.Writer) {
28 | Trace = log.New(traceHandle, "TRACE: ",
29 | log.Ldate|log.Ltime|log.Lshortfile)
30 |
31 | Info = log.New(infoHandle, "INFO: ",
32 | log.Ldate|log.Ltime|log.Lshortfile)
33 |
34 | Warning = log.New(warningHandle, "WARNING: ",
35 | log.Ldate|log.Ltime|log.Lshortfile)
36 |
37 | Error = log.New(errorHandle, "ERROR: ",
38 | log.Ldate|log.Ltime|log.Lshortfile)
39 | }
40 |
41 | // type UtilLogger {
42 | // _trace *log.Logger
43 | // _info *log.Logger
44 | // _warning *log.Logger
45 | // _error *log.Logger
46 | // }
47 |
48 | // func (logger UtilLogger *) New(traceHandle io.Writer, infoHandle io.Writer, warningHandle io.Writer, errorHandle io.Writer) {
49 | // logger._trace = log.New(traceHandle, "TRACE: ",
50 | // log.Ldate|log.Ltime|log.Lshortfile)
51 |
52 | // logger._info = log.New(infoHandle, "INFO: ",
53 | // log.Ldate|log.Ltime|log.Lshortfile)
54 |
55 | // logger._warning = log.New(warningHandle, "WARNING: ",
56 | // log.Ldate|log.Ltime|log.Lshortfile)
57 |
58 | // logger._error = log.New(errorHandle, "ERROR: ",
59 | // log.Ldate|log.Ltime|log.Lshortfile)
60 |
61 | // }
62 |
63 | // func (logger UtilLogger *) Info(msg) {
64 | // logger._info.Println(msg)
65 | // }
66 |
--------------------------------------------------------------------------------
/storage/storage_example_test.go:
--------------------------------------------------------------------------------
1 | package storage_test
2 |
3 | import (
4 | "crypto/rand"
5 | "fmt"
6 | "io"
7 | "os"
8 |
9 | ss "github.com/ibm-security-innovation/libsecurity-go/storage"
10 | )
11 |
12 | const (
13 | keyFmt = "The key is: %v"
14 | dataFmt = "The data is (sum of key digits): %v"
15 | )
16 |
17 | var (
18 | aesSecretLen = 32
19 | )
20 |
21 | func init() {
22 | }
23 |
24 | func generateSecureStorage() (*ss.SecureStorage, []byte) {
25 | var secret []byte
26 |
27 | secret = make([]byte, aesSecretLen)
28 | io.ReadFull(rand.Reader, secret)
29 | storage, _ := ss.NewStorage(secret, false)
30 | for i := 0; i < 10; i++ {
31 | keyText := fmt.Sprintf(keyFmt, i)
32 | dataText := fmt.Sprintf(dataFmt, i*10+1)
33 | storage.AddItem(keyText, dataText)
34 | }
35 | return storage, secret
36 | }
37 |
38 | func playWithSecureStorage(storage *ss.SecureStorage, secret []byte) {
39 | fileName := "try.txt"
40 | defer os.Remove(fileName)
41 | err := storage.StoreInfo(fileName)
42 | if err != nil {
43 | fmt.Println("Error while saving:", err)
44 | }
45 |
46 | fmt.Println("Original data:")
47 | fmt.Println(storage.GetDecryptStorageData())
48 |
49 | keyText := fmt.Sprintf(keyFmt, 1)
50 | err = storage.RemoveItem(keyText)
51 | if err != nil {
52 | fmt.Println("Error while remove key:", err)
53 | }
54 |
55 | fmt.Println("After removing:")
56 | fmt.Println(storage.GetDecryptStorageData())
57 |
58 | sd, err := ss.LoadInfo(fileName, secret)
59 | if err != nil {
60 | fmt.Println("Error while reading:", err)
61 | }
62 | fmt.Println("The data that was read from file:", fileName)
63 | fmt.Println(sd.GetDecryptStorageData())
64 | }
65 |
66 | // This example shows how to create a new secure storage list.
67 | // 1. Add 10 new items with the following format: key: "The key is: %v", Value: "The data is (sum of key digits): %v"
68 | // 2. Print it
69 | // 3. Save it to file
70 | // 4. Remove 1 item from it
71 | // 5. Print it again
72 | // 6. Read the saved storage
73 | // 7. Print it again
74 | func Example_storage() {
75 | storage, secret := generateSecureStorage()
76 | playWithSecureStorage(storage, secret)
77 | }
78 |
--------------------------------------------------------------------------------
/defs/libsecurityCommonForTest.go:
--------------------------------------------------------------------------------
1 | // Package defs : This package is responsible for the common tests
2 | package defs
3 |
4 | import (
5 | "io/ioutil"
6 | "testing"
7 |
8 | logger "github.com/ibm-security-innovation/libsecurity-go/logger"
9 | ss "github.com/ibm-security-innovation/libsecurity-go/storage"
10 | )
11 |
12 | // StoreLoadTest : common store load testing for all the different properties
13 | func StoreLoadTest(t *testing.T, userData interface{}, propertyName string) {
14 | filePath := "./tmp.txt"
15 | key := "key"
16 | secret := []byte("12345678")
17 | logger.Init(ioutil.Discard, ioutil.Discard, ioutil.Discard, ioutil.Discard)
18 |
19 | storage, err := ss.NewStorage(secret, false)
20 | if err != nil {
21 | t.Errorf("Fatal error: can't create storage, error: %v", err)
22 | }
23 | s := Serializers[propertyName]
24 | err = s.AddToStorage(key, userData, storage)
25 | if err != nil {
26 | t.Fatal("Fatal error: can't add to storage, error:", err)
27 | }
28 | err = s.AddToStorage(key, nil, storage)
29 | if err == nil {
30 | t.Errorf("Test fail: Successfully add undefined property to storage")
31 | }
32 | err = s.AddToStorage(key, userData, nil)
33 | if err == nil {
34 | t.Errorf("Test fail: Successfully add property to nil storage")
35 | }
36 | storage.StoreInfo(filePath)
37 | storage, err = ss.LoadInfo(filePath, secret)
38 | if err != nil {
39 | t.Fatal("Fatal error: can't load from storage, error:", err)
40 | }
41 | _, err = s.ReadFromStorage(key, nil)
42 | loadStorage := storage.GetDecryptStorageData()
43 |
44 | if err == nil {
45 | t.Fatal("Fatal error: Read pass but storage is nil")
46 | }
47 | _, err = s.ReadFromStorage("", loadStorage)
48 | if err == nil {
49 | t.Fatal("Fatal error: Read pass but the key is empty")
50 | }
51 | _, err = s.ReadFromStorage(key, loadStorage)
52 | if err != nil {
53 | t.Fatal("Fatal error: can't load from storage, error:", err)
54 | }
55 | data, err := s.ReadFromStorage(key, loadStorage)
56 | if err != nil {
57 | t.Fatal("Fatal error: can't read from storage, error:", err)
58 | }
59 | if s.IsEqualProperties(userData, data) == false {
60 | t.Fatal("Fatal error: Data read from storage:", s.PrintProperties(data), "is not equal to the one that was write to storage:", userData)
61 | }
62 | if s.IsEqualProperties(userData, "") == true {
63 | t.Fatal("Fatal error: unequal properies were found equal")
64 | }
65 | logger.Trace.Println("Data:", s.PrintProperties(data))
66 | }
67 |
--------------------------------------------------------------------------------
/restful/libsecurity-restful/libsecurity_command.go:
--------------------------------------------------------------------------------
1 | package libsecurityRestful
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 |
7 | "github.com/emicklei/go-restful"
8 | cr "github.com/ibm-security-innovation/libsecurity-go/restful/common-restful"
9 | )
10 |
11 | const (
12 | handleFileCommand = iota
13 | handleCommand
14 | )
15 |
16 | var (
17 | // Version : the libsecurity version
18 | Version = "2.2"
19 | // MinVersion : the libsecurity sub version
20 | MinVersion = ".0008"
21 |
22 | urlCommands = make(cr.CommandToPath)
23 |
24 | commandsToPath = []cr.ComamndsToPath{
25 | {handleFileCommand, "%v"},
26 | {handleCommand, "%v"},
27 | }
28 |
29 | lock sync.Mutex
30 | )
31 |
32 | // GetVersion : return the libsecurity version as a string
33 | func GetVersion() string {
34 | return fmt.Sprintf("%s:%s%s", "Security Tool version", Version, MinVersion)
35 | }
36 |
37 | func initCommandToPath() {
38 | for _, c := range commandsToPath {
39 | urlCommands[c.Command] = c.Path
40 | }
41 | }
42 |
43 | func (s LibsecurityRestful) loadStroreRoute(service *restful.WebService) {
44 | str := fmt.Sprintf(urlCommands[handleFileCommand], storePath)
45 | service.Route(service.PUT(str).
46 | Filter(s.SuperUserFilter).
47 | To(s.restStoreData).
48 | Doc("Store Security Tool data to file").
49 | Operation("updateLibsecurityDataFile").
50 | Reads(cr.SecureFile{}).
51 | Writes(cr.StringMessage{}))
52 |
53 | str = fmt.Sprintf(urlCommands[handleFileCommand], loadPath)
54 | service.Route(service.PATCH(str).
55 | Filter(s.SuperUserFilter).
56 | To(s.restLoadData).
57 | Doc("Read Security Tool data from file").
58 | Operation("loadLibsecurityDataFile").
59 | Reads(cr.SecureFile{}))
60 | }
61 |
62 | func (s LibsecurityRestful) versionRoute(service *restful.WebService) {
63 | str := fmt.Sprintf(urlCommands[handleCommand], cr.VersionPath)
64 | service.Route(service.GET(str).
65 | // Filter(s.SuperUserFilter).
66 | To(s.restGetVersion).
67 | Doc("Get Security Tool version").
68 | Operation("getLibsecurityVersion").
69 | Writes(cr.StringMessage{}))
70 | }
71 |
72 | // RegisterBasic : register the libsecurity to the RESTFul API container
73 | func (s LibsecurityRestful) RegisterBasic(container *restful.Container) {
74 | servicePath = cr.ServicePathPrefix + cr.Version + stPrefix
75 | service := new(restful.WebService)
76 | service.
77 | Path(servicePath).
78 | Consumes(restful.MIME_JSON).
79 | Produces(restful.MIME_JSON).
80 | Doc("The Security Tool")
81 |
82 | s.loadStroreRoute(service)
83 | s.versionRoute(service)
84 | container.Add(service)
85 | }
86 |
--------------------------------------------------------------------------------
/acl/aclEntry.go:
--------------------------------------------------------------------------------
1 | package acl
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 |
7 | en "github.com/ibm-security-innovation/libsecurity-go/entity"
8 | )
9 |
10 | // PermissionsMap : hash to check if a premission was defined
11 | type PermissionsMap map[en.Permission]interface{}
12 |
13 | var pLock sync.Mutex
14 |
15 | // Entry : structure that holds the entity name and the set of permissions associated to this entry
16 | type Entry struct {
17 | EntityName string
18 | Permissions PermissionsMap
19 | }
20 |
21 | func (a Entry) String() string {
22 | return fmt.Sprintf("Name: %v, permissions: %v", a.EntityName, a.Permissions)
23 | }
24 |
25 | func isPermissionValid(permission en.Permission) error {
26 | if len(permission) == 0 {
27 | return fmt.Errorf("Permission is not valid: Length must be larger than 0")
28 | }
29 | return nil
30 | }
31 |
32 | // NewEntry : Generate a new ACL entry structure
33 | func NewEntry(name string) (*Entry, error) {
34 | err := en.IsEntityNameValid(name)
35 | if err != nil {
36 | return nil, err
37 | }
38 | a := Entry{EntityName: name, Permissions: make(PermissionsMap)}
39 | return &a, nil
40 | }
41 |
42 | // AddPermission : If the permission is valid and was not set yet, add it to the entry's permission list
43 | func (a *Entry) AddPermission(permission en.Permission) (bool, error) {
44 | pLock.Lock()
45 | defer pLock.Unlock()
46 |
47 | err := isPermissionValid(permission)
48 | if err != nil {
49 | return false, err
50 | }
51 | _, exist := a.Permissions[permission]
52 | if exist {
53 | return false, fmt.Errorf("Cannot add permission: '%v', it already exists in the permission list", permission)
54 | }
55 | a.Permissions[permission] = ""
56 | return true, nil
57 | }
58 |
59 | // RemovePermission : Remove the given permission from the ACL entry
60 | func (a *Entry) RemovePermission(permission en.Permission) error {
61 | pLock.Lock()
62 | defer pLock.Unlock()
63 |
64 | err := isPermissionValid(permission)
65 | if err != nil {
66 | return err
67 | }
68 | _, exist := a.Permissions[permission]
69 | if exist == false {
70 | return fmt.Errorf("Cannot remove permission: '%v', it does not exist in the permission list", permission)
71 | }
72 | delete(a.Permissions, permission)
73 | return nil
74 | }
75 |
76 | // CheckPermission : Check if a given permission is in the entry's list
77 | func (a Entry) CheckPermission(permission en.Permission) (bool, error) {
78 | pLock.Lock()
79 | defer pLock.Unlock()
80 |
81 | err := isPermissionValid(permission)
82 | if err != nil {
83 | return false, err
84 | }
85 | _, exist := a.Permissions[permission]
86 | return exist, nil
87 | }
88 |
--------------------------------------------------------------------------------
/salt/salt_examples_test.go:
--------------------------------------------------------------------------------
1 | package salt_test
2 |
3 | import (
4 | "crypto/md5"
5 | "fmt"
6 | "hash"
7 |
8 | "github.com/ibm-security-innovation/libsecurity-go/salt"
9 | )
10 |
11 | const (
12 | minSecretLen = 1
13 | maxSecretLen = 255
14 | )
15 |
16 | var (
17 | BasicSecret = []byte("ABCDABCD")
18 | BasicSalt = []byte("A1B2")
19 | )
20 |
21 | func getSaltExample(secret, sa []byte, iter int, f func() hash.Hash, size int) ([]byte, error) {
22 | mySalt, err := salt.NewSalt(secret, minSecretLen, maxSecretLen, sa)
23 | if err != nil {
24 | return nil, err
25 | }
26 | mySalt.Iterations = iter
27 | mySalt.OutputLen = size
28 | mySalt.Digest = f
29 | return mySalt.Generate(minSecretLen, maxSecretLen)
30 | }
31 |
32 | func getRandomSaltExample(secret []byte, saltLen int, iter int, f func() hash.Hash, size int) ([]byte, []byte, error) {
33 | salting, _ := salt.GetRandomSalt(saltLen)
34 | mySalt, err := salt.NewSalt(secret, minSecretLen, maxSecretLen, salting)
35 | if err != nil {
36 | return nil, nil, err
37 | }
38 | mySalt.Iterations = iter
39 | mySalt.OutputLen = size
40 | mySalt.Digest = f
41 | res, err := mySalt.Generate(minSecretLen, maxSecretLen)
42 | if err != nil {
43 | return nil, nil, err
44 | } else {
45 | return res, salting, nil
46 | }
47 | }
48 |
49 | // This example shows how to generate a saltetd password from a given password and default salt
50 | func ExampleGenerateSaltedPassword() {
51 | pass := "MyPassword"
52 | res, err := salt.GenerateSaltedPassword([]byte(pass), minSecretLen, maxSecretLen, BasicSalt, -1)
53 | if err != nil {
54 | fmt.Println("GenerateSaltedPassword failed, error:", err)
55 | } else {
56 | fmt.Printf("* Generate basic salted password from a given password: '%v', using the default parameters (sha1, show full password, 1 iteration and random salt: %v) is: %v", pass, BasicSalt, res)
57 | }
58 | }
59 |
60 | // This example shows how to generate a randomly salted secret contain 8 characters, using hash function of md5 and 3 iterations of calculations
61 | func ExampleGetRandomSalt() {
62 | iter := 3
63 | f := md5.New
64 | size := 8
65 | saltLen := 32
66 |
67 | randSalt, _ := salt.GetRandomSalt(saltLen)
68 | mySalt, err := salt.NewSalt(BasicSecret, minSecretLen, maxSecretLen, randSalt)
69 | if err != nil {
70 | fmt.Println("Error while creating the new salt structure:", err)
71 | }
72 | mySalt.Iterations = iter
73 | mySalt.OutputLen = size
74 | mySalt.Digest = f
75 | res, err := mySalt.Generate(minSecretLen, maxSecretLen)
76 | if err != nil {
77 | fmt.Println("GetRandomSaltExample failed with error:", err)
78 | } else {
79 | fmt.Println("* Salted password of secret key:", string(BasicSecret), ",random salt length:", saltLen, randSalt,
80 | "with", iter, "iterations, output password length:", size, "bytes and MD5 function is:", res)
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/swagger-dist/forewind/app/v1/libsecurity:
--------------------------------------------------------------------------------
1 | {
2 | "swaggerVersion": "1.2",
3 | "apiVersion": "",
4 | "basePath": "/",
5 | "resourcePath": "/forewind/app/v1/libsecurity",
6 | "apis": [
7 | {
8 | "path": "/forewind/app/v1/libsecurity/store",
9 | "description": "The Security Tool",
10 | "operations": [
11 | {
12 | "type": "common_restful.StringMessage",
13 | "method": "PUT",
14 | "summary": "Store Security Tool data to file",
15 | "nickname": "updateLibsecurityDataFile",
16 | "parameters": [
17 | {
18 | "type": "common_restful.SecureFile",
19 | "paramType": "body",
20 | "name": "body",
21 | "description": "",
22 | "required": true,
23 | "allowMultiple": false
24 | }
25 | ],
26 | "produces": [
27 | "application/json"
28 | ],
29 | "consumes": [
30 | "application/json"
31 | ]
32 | }
33 | ]
34 | },
35 | {
36 | "path": "/forewind/app/v1/libsecurity/load",
37 | "description": "The Security Tool",
38 | "operations": [
39 | {
40 | "type": "void",
41 | "method": "PATCH",
42 | "summary": "Read Security Tool data from file",
43 | "nickname": "loadLibsecurityDataFile",
44 | "parameters": [
45 | {
46 | "type": "common_restful.SecureFile",
47 | "paramType": "body",
48 | "name": "body",
49 | "description": "",
50 | "required": true,
51 | "allowMultiple": false
52 | }
53 | ],
54 | "produces": [
55 | "application/json"
56 | ],
57 | "consumes": [
58 | "application/json"
59 | ]
60 | }
61 | ]
62 | },
63 | {
64 | "path": "/forewind/app/v1/libsecurity/version",
65 | "description": "The Security Tool",
66 | "operations": [
67 | {
68 | "type": "common_restful.StringMessage",
69 | "method": "GET",
70 | "summary": "Get Security Tool version",
71 | "nickname": "getLibsecurityVersion",
72 | "parameters": [],
73 | "produces": [
74 | "application/json"
75 | ],
76 | "consumes": [
77 | "application/json"
78 | ]
79 | }
80 | ]
81 | }
82 | ],
83 | "models": {
84 | "common_restful.SecureFile": {
85 | "id": "common_restful.SecureFile",
86 | "required": [
87 | "FilePath",
88 | "Secret"
89 | ],
90 | "properties": {
91 | "FilePath": {
92 | "type": "string"
93 | },
94 | "Secret": {
95 | "type": "string"
96 | }
97 | }
98 | },
99 | "common_restful.StringMessage": {
100 | "id": "common_restful.StringMessage",
101 | "required": [
102 | "Str"
103 | ],
104 | "properties": {
105 | "Str": {
106 | "type": "string"
107 | }
108 | }
109 | }
110 | }
111 | }
--------------------------------------------------------------------------------
/swagger-dist/key.private:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIJJwIBAAKCAgEAwjDGUTPehq+jHzhPJzGv/W9w12cn8j4p4E7fU8WyCXKKlI7b
3 | L6TAnFre53mkd7Eqq1+4Rsg7lZOp7nDd2yFnyxz5QIPycc3WMc/DRgS0Q65U55LR
4 | Sln9k/T3NzSzU8nwyMXNqkSlFaLSrGb3E9zgK3HUlFUfk+gDqS3V/mFcV4jnaoTC
5 | Y2r7MRKMvNlMTVIelx/6dNTcQKqKKI3YpLVeF7Sh6KH1ppxmprrAxVmFcMSGLHEK
6 | ci9J7+mWB1fQTHOh7aJ2Kzzec7BNWNbQYIil7iE+bO0CjvpUacpGpAb8EY6PaOMe
7 | gvuw+QX3R3UciYKEm8E+fUr8cebF3S4h7Aq3ZWbh9xKL2c5vnp3BokFUKJFv2Ulp
8 | WBcvXjkVIZQifk50h4ctoS9MTlZ7Z3sxLylkHX3w/BDJzzhSoKkWnvxHhiLvVmmy
9 | bZZPsKLQU9gykpmqUgbHalvD4tfaiyDZysl5drS1TdmdDOnoCHxb8b2+07cUS1aa
10 | 0VAt1bmPagleDzjGMdyW7Sl5ZYgMsnHJaQg6m0ClEm6EbKhRBrinoUxEy3uhMBGi
11 | 7tbEBU+nEOd5Mb8qNAHzTvQbdPpBHObYEtvPNQ9PSVhx4ZvF2g66WLQP18ydL398
12 | +9sqQ4IPCdy25wRKLqMk8vqjVWGDYWgm0H/I6DLthgaYkmakgKWlZtQ0hokCAwEA
13 | AQKCAgBByfp2Kkd6g+iUvCP82FWSW/3zzAv5gtv3FWGN5Q1TLA4NFHTnqD5k+lhI
14 | PardrhURqEUrHc0p+LP6hxxyj9e1NWSwWcN6kMy/ZW73usJDp702E0R6lxBU3woe
15 | FummOmUr7++kfssbEGWlaODORudfcJofgtNG7bTHzTKRkvSQ513XdZDR0720O1+Y
16 | 31l9XJPmiTv19uODuQNDiALckmGFUHoF8VDjGFuiouSKN7EAqmP2jHmIg8Ld19ov
17 | NK3qKMa0VnUIYv/OW+1oaqZ//euRSIv4pTdXrzGIAbJ7Aa7GV/D4DPIaWCD1zPKg
18 | r+lyINjCV+7OzefGwvNH4sg1StAkoGVmmBQ8Moq7kORLNrVv5O0PNK0+2Z5mGqsm
19 | TwnDmxu3sQ+1jw2+gJMiKvWYk9+FQyA403Odqdp7MxY5mo3L/shxT+65kUTBQRsr
20 | /pInuObcl13yzB3Lb8t6ukYLqjkm1g5kQwQzq+mzcTzFRD9uNTTZekZE7hKM5Jw+
21 | dmylefVL2pK/0wL48H9BqgfcoeAxq3banE0JphAKxLom2n/XigaOMjNSfYfkfAf4
22 | sb0jFuPUP6xHJar5zzBwuXGu4jl54BVbUVhDDXIDJU2fxHnrj8rVRd3Y4IY/UccZ
23 | UbSMivyf0sFBCRRljLU7yVu0/t0J+ZGgfH0OeopXLdS5tYBMgQKCAQEA25Ejl/vd
24 | 4jQKOpZirbfV7Rgx7OdHLvJhjaXCS5g/mW6UxwDAnpEWAt6LJZ0ZhhMDvfUc4evW
25 | igz6mTTe0ld1fBKmv3NxmHpXoZUE3jbIVIXTlqyXj5RMZJ3/O29UuBIHMJlG+MfQ
26 | BcsaoGkuqnzMfZiDJRCD0Heg5Iv1hHelHzrIC0TLYahu2xxmdcJ29snbLoZaAUxa
27 | hEaUqVlzSNeZWUCnKLrNlcTHw0cXCKt3deyNT5f5fmfrtsM/4RSPsFesaNUcclQj
28 | OHzgb0Fgd+VYYdqfHkEAXQsGcljBuJo7AZWjKgW7+DBZegpJ2c7VD5mhVZB0W2s7
29 | TcygvBx6AxMO0QKCAQEA4mmvULL2e7N0nXdSZ696iwL27sico/dlMCdVS9Y1YX8S
30 | qw8uLSS5cnWpZ8OR/q3eg1SkQdQrazNWpggjMoLtcbE6hxc5JYcByy+O7Ljspt0m
31 | +v0V8ooOmOIB0OgaodeWRBHC81JaawJn9ll3F61pw43mNGEpU03lJYaVruQWJ5RF
32 | QfM/SdqItXINo3t+k031QzJyhMRtAeGM+TEbIZ2TPcJ6uB4SF/sCPTF9iNClkQEb
33 | DSkoTxvsTeCVgOBGoIDoEN+H37LnAuTLvdVnlyErZsQeqcSaoATKYkvh0Jcj82FH
34 | DMYdGQ3wiNB+ux7S8y0iVKs0/sNeXZZIq+5qzWUaOQKCAQAXQ6mh0O4RqbHrw0Nx
35 | ZKkmFgo0L807DDOJ7qZHnDog57ab31iJdJYAd9QGiD7MFfeviOkmhvaUagujcBar
36 | 8thf7Rn97x16Knxegouhf+x6CDQDIwZsECqH/rWsdn9jgYWBgLwkYAeKlZqcpIht
37 | SAviwuYiZz2QORyzGqFJB+RXCsNnbkKedlpInRm+IHy6aWjv5QxkVtLAcToPN4sf
38 | jLiS3GbZyfE+jTsdVcr7ADAILqhKZq/o1NoE42tBTgxrgt6hvPLmqkORGKovH545
39 | FfPnamKxsfF36lMkioL8LFtUL6a6LSFdisHwTGbGzUOGA6E0j7nEdfgyphvh0qKa
40 | 8MoBAoIBAB0KXThGnCLls7F22pT+BwPJj461IJJPayUwBlrB0Pr+Z5CVU+Dqcvbh
41 | bKcoiJNYDdBejOr8oc0QOq71ID6cWIRarR0it2u+01YSdYybc487koJi7+u6FYE6
42 | i+0VuETF+Dom5RB8oahUGK2acBITZht1Ge/h/oD23stfZdeW3diZxgBdiOKaKyrK
43 | xEER1/xg5XKHB/VLlIfHPGPCDq7THU2wojj3cm6s/W6H6Igh6vqcX7Hic48iD781
44 | bTeojKaNojZ31V+1/Ld8TT2+H5lc4co+oYg49XRM6QCOpxowKKZQ9eIHjzfld9K0
45 | m2y42WnjgiHGC/gZawMqAYw7+mr56tECggEAMQmMjing1/0dpIeVOQbAUuLAkvDt
46 | /IREJzD/30k5a+Fa5ihLFjrmHIGKEGwRaD8VKkCVQN7Go3ukgainTIjIj1gHqRAL
47 | uaqWM0OO6Wq0oM32cwrgBPRIPKRrKLtWCKH2stSRYU+itRhurUGmcgWc2q6HWsC5
48 | ew//9eE/4MqIdFcgA+MLR+1WmkMB/t5zahrD5sxTjRqvy1NBmCTQtgXxdE8zho6y
49 | ibKL04xId+vjbqlUaPocmMLMocHzH3fzfgoSbBGrf/g0O3CDI12Ei1gWjQqqDGyY
50 | 0hnWCJi47YYq0yuDuUMolk+lWzTK9yJlG8Bnbowp2NXKQBK2646O7EoyZw==
51 | -----END RSA PRIVATE KEY-----
52 |
--------------------------------------------------------------------------------
/app/token/token_test.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import (
4 | // "fmt"
5 | "testing"
6 |
7 | am "github.com/ibm-security-innovation/libsecurity-go/accounts"
8 | defs "github.com/ibm-security-innovation/libsecurity-go/defs"
9 | )
10 |
11 | const (
12 | defaultUserName = "User1"
13 | defaultIP = "1.2.3.4"
14 |
15 | privateKeyFilePath = "./dist/key.private"
16 | )
17 |
18 | var ()
19 |
20 | func init() {
21 | }
22 |
23 | // Verify that only the same tokens are equal
24 | func Test_GenerateToken(t *testing.T) {
25 | usersName := []string{defaultUserName, defaultUserName + "a", ""}
26 | ipsStr := []string{defaultIP, defaultIP + "1", ""}
27 | usersPrivilege := am.GetUsersPrivilege()
28 | updatePassword := []bool{true, false}
29 | signKey, verifyKey := SetupAToken(privateKeyFilePath)
30 |
31 | for _, name := range usersName {
32 | for _, ip := range ipsStr {
33 | for p := range usersPrivilege {
34 | for _, u := range updatePassword {
35 | token1, _ := GenerateToken(name, p, u, ip, signKey)
36 | data, err := ParseToken(token1, ip, verifyKey)
37 | if err != nil || data.UserName != name || data.Privilege != p || data.UpdatePassword != u {
38 | t.Errorf("Test fail: the parsed token != generated token, error: %v", err)
39 | }
40 | _, err = ParseToken(token1, ip+"1", verifyKey)
41 | if err == nil {
42 | t.Errorf("Test fail: return successful from parsed token but the IPs are different")
43 | }
44 | }
45 | }
46 | }
47 | }
48 | }
49 |
50 | // Verify that only user with the relevant privilege can run the operation
51 | // Verify that the IsTheSameUser return the expected value
52 | func Test_CheckPrivilegeAndSameUser(t *testing.T) {
53 | groupsName := []string{defs.UsersGroupName, defs.AdminGroupName, defs.SuperUserGroupName}
54 | usersName := []string{"user", "admin", "super"}
55 | permissions := []string{am.UserPermission, am.AdminPermission, am.SuperUserPermission}
56 |
57 | signKey, verifyKey := SetupAToken(privateKeyFilePath)
58 |
59 | for _, name := range usersName {
60 | usersList.AddUser(name)
61 | }
62 | for i, name := range groupsName {
63 | usersList.AddGroup(name) // to be on the safe side
64 | for j := i; j < len(usersName); j++ {
65 | usersList.AddUserToGroup(name, usersName[j])
66 | }
67 | }
68 | for i, name := range usersName {
69 | token1, _ := GenerateToken(name, permissions[i], true, defaultIP, signKey)
70 | for j := range usersName {
71 | ok, err := IsPrivilegeOk(token1, permissions[j], defaultIP, verifyKey)
72 | if j > i && ok == true {
73 | t.Errorf("Test fail: Is privilege returns %v but the token privilege is '%v' and the check was for '%v', error %v", ok, usersName[i], permissions[j], err)
74 | } else if j <= i && ok == false {
75 | t.Errorf("Test fail: Is privilege returns %v but the token privilege is '%v' and the check was for '%v', error %v", ok, usersName[i], permissions[j], err)
76 | }
77 | }
78 | }
79 | for _, name1 := range usersName {
80 | token1, _ := GenerateToken(name1, name1, true, defaultIP, signKey)
81 | for _, name2 := range usersName {
82 | ok, _ := IsItTheSameUser(token1, name2, defaultIP, verifyKey)
83 | if name1 != name2 && ok == true {
84 | t.Errorf("Test fail: IsItTheSameUser returns true but the names are different: '%v', '%v'", name1, name2)
85 | } else if name1 == name2 && ok == false {
86 | t.Errorf("Test fail: IsItTheSameUser returns false but the names are the same: '%v', '%v'", name1, name2)
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/acl/acl_example_test.go:
--------------------------------------------------------------------------------
1 | package acl_test
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/ibm-security-innovation/libsecurity-go/acl"
7 | defs "github.com/ibm-security-innovation/libsecurity-go/defs"
8 | en "github.com/ibm-security-innovation/libsecurity-go/entity"
9 | )
10 |
11 | const (
12 | userName1 = "User1"
13 | userName2 = "User2"
14 | resourceName = "Camera"
15 | userInGroupName1 = "gUser1"
16 | userInGroupName2 = userName2
17 | groupName = "support"
18 | canUsePermission = "Can use"
19 | allPermission = "All can use it"
20 | usersPermission = "for users only"
21 | supportPermission = "Can take"
22 | unsetPermission = "This permission is not allowed"
23 | )
24 |
25 | var (
26 | usersName = []string{userName1, userName2}
27 | groupUsersName = []string{userInGroupName1, userInGroupName2}
28 | )
29 |
30 | func initEntityManager() *en.EntityManager {
31 | entityManager := en.New()
32 | for _, name := range usersName {
33 | entityManager.AddUser(name)
34 | }
35 | entityManager.AddGroup(groupName)
36 | for _, name := range groupUsersName {
37 | entityManager.AddUser(name)
38 | entityManager.AddUserToGroup(groupName, name)
39 | }
40 | entityManager.AddResource(resourceName)
41 | a := acl.NewACL()
42 | entityManager.AddPropertyToEntity(resourceName, defs.AclPropertyName, a)
43 | return entityManager
44 | }
45 |
46 | // Shows how to add/check/remove permissions for a n entity (resource) of a user or a group entity
47 | func Example_acl() {
48 | entityManager := initEntityManager()
49 | fmt.Println("ExampleShowACLAddCheckRemovePermissions")
50 | fmt.Printf("User %q permission %q is %v\n", userName1, canUsePermission,
51 | acl.CheckUserPermission(entityManager, userName1, resourceName, en.Permission(canUsePermission)))
52 | data, _ := entityManager.GetPropertyAttachedToEntity(resourceName, defs.AclPropertyName)
53 | a, ok := data.(*acl.Acl)
54 | if ok == false {
55 | fmt.Println("Error: Cannot get property", defs.AclPropertyName, "attached to resource", resourceName)
56 | return
57 | }
58 | a.AddPermissionToEntity(entityManager, userName1, en.Permission(canUsePermission))
59 | fmt.Printf("User %q permission %q is: %v\n", userName1, canUsePermission,
60 | acl.CheckUserPermission(entityManager, userName1, resourceName, en.Permission(canUsePermission)))
61 | a.AddPermissionToEntity(entityManager, groupName, en.Permission(supportPermission))
62 | a.AddPermissionToEntity(entityManager, groupName, en.Permission(canUsePermission))
63 | a.AddPermissionToEntity(entityManager, defs.AclAllEntryName, en.Permission(allPermission))
64 | a.AddPermissionToEntity(entityManager, userInGroupName1, en.Permission(usersPermission))
65 | permissions, _ := acl.GetUserPermissions(entityManager, userInGroupName1, resourceName)
66 | fmt.Printf("All the permissions for user %q on resource %q are: %q\n",
67 | userInGroupName1, resourceName, permissions)
68 | permissions, _ = acl.GetUserPermissions(entityManager, groupName, resourceName)
69 | fmt.Printf("All the permissions for group %q on resource %q are: %q\n", groupName, resourceName, permissions)
70 | a.RemovePermissionFromEntity(groupName, en.Permission(canUsePermission))
71 | fmt.Printf("After remove permission: %q from group %q\n", canUsePermission, groupName)
72 | fmt.Printf("User %q permission %q is: %v\n", userInGroupName1, canUsePermission,
73 | acl.CheckUserPermission(entityManager, userInGroupName1, resourceName, en.Permission(canUsePermission)))
74 | fmt.Printf("All the permissions are: %q\n", a.GetAllPermissions())
75 | }
76 |
--------------------------------------------------------------------------------
/password/password_example_test.go:
--------------------------------------------------------------------------------
1 | package password_test
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/ibm-security-innovation/libsecurity-go/password"
7 | "github.com/ibm-security-innovation/libsecurity-go/salt"
8 | )
9 |
10 | var (
11 | minPasswordLength = 1
12 | maxPasswordLength = 255
13 | )
14 |
15 | // Example of how to use the password.
16 | // 1. Create a new password.
17 | // 2. Verify that the initial password is set correctly
18 | // 3. Change the user's password
19 | // 4. Verify that the old password is not valid anymore
20 | // 5. Verify that the new password is valid
21 | // 6. Verify that the old password can't be used any more
22 | // (at least not as long as it remains in the old passwords list)
23 | func ExampleUserPwd() {
24 | id := "User-1"
25 | pwd := []byte("a1B2c3d^@")
26 | saltStr, _ := salt.GetRandomSalt(8)
27 |
28 | userPwd, _ := password.NewUserPwd(pwd, saltStr, true)
29 | tPwd, _ := salt.GenerateSaltedPassword(pwd, minPasswordLength, maxPasswordLength, saltStr, -1)
30 | newPwd := password.GetHashedPwd(tPwd)
31 | err := userPwd.IsPasswordMatch(newPwd)
32 | if err != nil {
33 | fmt.Println("Error", err)
34 | }
35 | userNewPwd := []byte(string(pwd) + "a")
36 | newPwd, err = userPwd.UpdatePassword(userPwd.Password, userNewPwd, true)
37 | if err != nil {
38 | fmt.Printf("Password update for user %v to new password '%v' (%v) failed, error %v\n", id, newPwd, string(userNewPwd), err)
39 | } else {
40 | fmt.Printf("User '%v', updated password to '%v' (%v)\n", id, newPwd, string(userNewPwd))
41 | }
42 | err = userPwd.IsPasswordMatch(newPwd)
43 | if err != nil {
44 | fmt.Printf("Check of the new password, '%v' (%v), for user %v failed, error %v\n", newPwd, string(userNewPwd), id, err)
45 | } else {
46 | fmt.Printf("User '%v', new password '%v' (%v) verified successfully\n", id, newPwd, string(userNewPwd))
47 | }
48 | err = userPwd.IsPasswordMatch(pwd)
49 | if err == nil {
50 | fmt.Printf("Error: Old password '%v' (%v) for user %v accepted\n", pwd, string(pwd), id)
51 | } else {
52 | fmt.Printf("User '%v', Note that the old password '%v' (%v) cannot be used anymore\n", id, pwd, string(pwd))
53 | }
54 | newPwd, err = userPwd.UpdatePassword(userPwd.Password, pwd, true)
55 | if err == nil {
56 | fmt.Printf("Error: Password '%v' (typed password %v) for user %v was already used\n", newPwd, string(pwd), id)
57 | } else {
58 | fmt.Printf("Entity '%v'. Note that the old password (entered password) %v was already used\n", id, string(pwd))
59 | }
60 | }
61 |
62 | // Example of how to use the reset password function:
63 | // This function resets the current password,
64 | // selects a new password with short expiration time
65 | // and lets the user use it exactly once
66 | func ExampleUserPwd_ResetPassword() {
67 | id := "User1"
68 | pwd := []byte("a1b2C@3d4")
69 |
70 | saltStr, _ := salt.GetRandomSalt(10)
71 | userPwd, _ := password.NewUserPwd(pwd, saltStr, false)
72 | tmpPwd, _ := userPwd.ResetPassword()
73 | tPwd, _ := salt.GenerateSaltedPassword(tmpPwd, 1, 100, saltStr, -1)
74 | newPwd := password.GetHashedPwd(tPwd)
75 | err := userPwd.IsPasswordMatch(newPwd)
76 | if err != nil {
77 | fmt.Printf("Check of newly generated password '%v' for user %v failed, error %v\n", newPwd, id, err)
78 | } else {
79 | fmt.Printf("Entity %v, after resetting password '%v' verified successfully\n", id, newPwd)
80 | }
81 | err = userPwd.IsPasswordMatch(newPwd)
82 | if err == nil {
83 | fmt.Printf("Error: Newly generated password '%v' could be used only once\n", newPwd)
84 | } else {
85 | fmt.Printf("Newly generated password '%v', for entity %v, can only be used once\n", newPwd, id)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/otp/otpTotp_test.go:
--------------------------------------------------------------------------------
1 | package otp
2 |
3 | import (
4 | "testing"
5 | "time"
6 | )
7 |
8 | func testGetTotp(t *testing.T) *Totp {
9 | totp, err := NewTotp(BaseSecret)
10 | if err != nil {
11 | t.Error("Test fail,", err)
12 | t.FailNow()
13 | }
14 | return totp
15 | }
16 |
17 | // Verify that an error is generated only for an illegal lifespan
18 | func Test_TotpIllegalInterval(t *testing.T) {
19 | totp := testGetTotp(t)
20 |
21 | for i := 0; i < maxIntervalSec+2; i++ {
22 | totp.Interval = time.Second * time.Duration(i)
23 | _, err := totp.Now()
24 |
25 | if err == nil && (i < minIntervalSec || i > maxIntervalSec) { // The next error does not make sense to me:
26 | t.Errorf("Totp test failed: initialization was done for illegal interval duration %v, valid range should be (%vs-%vs)",
27 | time.Second*time.Duration(i), minIntervalSec, maxIntervalSec)
28 | } else if err != nil && (i >= minIntervalSec && i <= maxIntervalSec) {
29 | t.Errorf("Totp test failed: Otp init fail for legal interval duration %vs, error: %v", i, err)
30 | }
31 | }
32 | }
33 |
34 | // Verify that Different parameters (secret keys, digits, digests) result with different generated OTPs
35 | func Test_OtpOTPParamesChanged(t *testing.T) {
36 | res := make(map[string]TotpRun)
37 | for _, data := range referenceRunsTotp {
38 | if _, exists := res[data.result]; exists {
39 | t.Error("Runs with different parameters but returns the same otp: ", res[data.result], data)
40 | } else {
41 | t.Log("New OTP data:", data, "was not found in structure, so add it")
42 | res[data.result] = data
43 | }
44 | }
45 | }
46 |
47 | func setStateTotp(ot *Totp, state TotpRun) {
48 | ot.BaseOtp.Secret = state.totp.BaseOtp.Secret
49 | ot.BaseOtp.digest = state.totp.BaseOtp.digest
50 | ot.BaseOtp.Digits = state.totp.BaseOtp.Digits
51 | ot.Interval = time.Second * state.totp.Interval
52 | }
53 |
54 | // Verify that increments within the defined lifespan of a TOTP result with identical OTPs and
55 | // time increments larger than the defined lifespan of a TOTP result with different OTPs
56 | func Test_OtpTestTotp(t *testing.T) {
57 | totp := testGetTotp(t)
58 |
59 | intervalCalc := interval[0]
60 | timeOffset := []time.Duration{0 * time.Second, intervalCalc / 2, intervalCalc + (1 * time.Second)}
61 |
62 | for _, offset := range timeOffset {
63 | for _, data := range referenceRunsTotp {
64 | setStateTotp(totp, data)
65 | val, err := data.totp.AtTime(data.at.Add(offset))
66 | if err != nil {
67 | t.Error("Test fail before running, illigal parameters:", err)
68 | continue
69 | }
70 | if offset < data.totp.Interval {
71 | if val != data.result {
72 | t.Error("OTP unexpectedly changed during defined lifespan :", data, "original run time -", data.at,
73 | "curent run time -", data.at.Add(offset), " time offset -", offset,
74 | "defined lifespan -", data.totp.Interval,
75 | ", original OTP:", data.result, "is different from the resultant OTP -", val)
76 | } else {
77 | t.Log("OTP value during defined lifespan", offset, "was not changed, as expected")
78 | }
79 | }
80 | //the == is risky: for race conditions
81 | if offset >= data.totp.Interval {
82 | if val == data.result {
83 | t.Error("OTP value did not expire when lifespan expired:", data,
84 | "original run time -", data.at, ", current run time", data.at.Add(offset),
85 | ", time offset -", offset, ", defined lifespan -", data.totp.Interval,
86 | "but the OTP calculated value was the same -", val)
87 | }
88 | } else {
89 | t.Log("OTP value out of defined lifespan", offset, "was changed, as expected")
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/restful/password-restful/pwd_command.go:
--------------------------------------------------------------------------------
1 | package passwordRestful
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/emicklei/go-restful"
7 | "github.com/ibm-security-innovation/libsecurity-go/password"
8 | cr "github.com/ibm-security-innovation/libsecurity-go/restful/common-restful"
9 | )
10 |
11 | const (
12 | handleUserCommand = iota
13 | verifyUserPasswordCommand
14 | resetUserPasswordCommand
15 | )
16 |
17 | var (
18 | commandsToPath = []cr.ComamndsToPath{
19 | {handleUserCommand, "%v/{%v}"},
20 | {verifyUserPasswordCommand, "%v/{%v}"},
21 | {resetUserPasswordCommand, "%v/{%v}/%v"},
22 | }
23 | urlCommands = make(cr.CommandToPath)
24 | )
25 |
26 | func initCommandToPath() {
27 | for _, c := range commandsToPath {
28 | urlCommands[c.Command] = c.Path
29 | }
30 | }
31 |
32 | func (p PwdRestful) setRoute(service *restful.WebService) {
33 | str := fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
34 | service.Route(service.PUT(str).
35 | Filter(p.st.SuperUserFilter).
36 | To(p.restAddPwd).
37 | Doc("Add Password").
38 | Operation("AddPwd").
39 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
40 | Reads(secretData{}).
41 | Writes(cr.URL{}))
42 |
43 | str = fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
44 | service.Route(service.GET(str).Filter(p.st.SameUserFilter).
45 | To(p.restGetPwd).
46 | Doc("Get Password").
47 | Operation("getPwd").
48 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
49 | Writes(password.UserPwd{}))
50 |
51 | str = fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
52 | service.Route(service.DELETE(str).
53 | Filter(p.st.SuperUserFilter).
54 | To(p.restDeletePwd).
55 | Doc("Remove Password").
56 | Operation("deletePwd").
57 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")))
58 |
59 | str = fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
60 | service.Route(service.PATCH(str).
61 | Filter(p.st.SameUserFilter).
62 | To(p.restUpdatePassword).
63 | Doc("Update Password").
64 | Operation("updatePassword").
65 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
66 | Reads(cr.UpdateSecret{}).
67 | Writes(cr.URL{}))
68 |
69 | str = fmt.Sprintf(urlCommands[verifyUserPasswordCommand], usersPath, userIDParam)
70 | service.Route(service.POST(str).
71 | Filter(p.st.SameUserFilter).
72 | To(p.restVerifyPassword).
73 | Doc("Verify that a given password is as expected").
74 | Operation("verifyPassword").
75 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
76 | Reads(secretData{}).
77 | Writes(cr.Match{}))
78 |
79 | str = fmt.Sprintf(urlCommands[resetUserPasswordCommand], usersPath, userIDParam, resetUserPwdPath)
80 | service.Route(service.POST(str).
81 | Filter(p.st.SuperUserFilter).
82 | To(p.restResetPassword).
83 | Doc("Reset password").
84 | Operation("resetPassword").
85 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
86 | Writes(secretData{}))
87 | }
88 |
89 | // RegisterBasic : register the Password to the RESTFul API container
90 | func (p PwdRestful) RegisterBasic(container *restful.Container) {
91 | servicePath = cr.ServicePathPrefix + cr.Version + pwdPrefix
92 |
93 | service := new(restful.WebService)
94 | service.
95 | Path(servicePath).
96 | Consumes(restful.MIME_JSON).
97 | Produces(restful.MIME_JSON)
98 | //.Doc("Password Management")
99 |
100 | p.setRoute(service)
101 | container.Add(service)
102 | }
103 |
--------------------------------------------------------------------------------
/otp/otp_examples_test.go:
--------------------------------------------------------------------------------
1 | package otp_test
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 |
7 | defs "github.com/ibm-security-innovation/libsecurity-go/defs"
8 | en "github.com/ibm-security-innovation/libsecurity-go/entity"
9 | "github.com/ibm-security-innovation/libsecurity-go/otp"
10 | )
11 |
12 | const (
13 | entityName = "Entity1"
14 | )
15 |
16 | var (
17 | entityManager *en.EntityManager
18 | secret = []byte("AaB1@CDABCD12341234")
19 | )
20 |
21 | func init() {
22 | entityManager = en.New()
23 | }
24 |
25 | func addOtpUser(id string, secret []byte, startCounter int64) {
26 | entityManager.AddUser(id)
27 | otpUser, _ := otp.NewOtpUser(secret, true, false, 10, 1, 100, 2, 0, startCounter)
28 | entityManager.AddPropertyToEntity(id, defs.OtpPropertyName, otpUser)
29 | }
30 |
31 | // The TOTP example demonstrates the following secenario:
32 | // Verify that the calculated TOTP for the current time
33 | // is as expected by the provider
34 | func ExampleTotp() {
35 | addOtpUser(entityName, secret, 0)
36 |
37 | totp, err := otp.NewTotp(secret)
38 | if err != nil {
39 | fmt.Println("TOTP cannot be initialized, Error: ", err)
40 | }
41 | code, err := totp.Now()
42 | if err != nil {
43 | fmt.Println("Cannot generate TOTP, error: ", err)
44 | } else {
45 | e, _ := entityManager.GetPropertyAttachedToEntity(entityName, defs.OtpPropertyName)
46 | if e == nil {
47 | return
48 | }
49 | entity, ok := e.(*otp.UserInfoOtp)
50 | if ok == true {
51 | ok, err := entity.VerifyOtpUserCode(code, otp.TotpType)
52 | if ok {
53 | fmt.Println("TOTP: Entity:", entityName, "Code:", code, "as expected")
54 | } else {
55 | fmt.Println("TOTP: Entity:", entityName, "Code:", code, "is not as expected, error:", err)
56 | }
57 | }
58 | }
59 | }
60 |
61 | type HotpUser struct {
62 | name string
63 | counter int64
64 | hotp *otp.Hotp
65 | }
66 |
67 | func (u HotpUser) String() string {
68 | ret := fmt.Sprintf("Entity id: %v, counter %v", u.name, u.counter)
69 | return ret
70 | }
71 |
72 | func initHotpUserslist(users []HotpUser, secret []byte) {
73 | for i, user := range users {
74 | addOtpUser(user.name, secret, user.counter)
75 | hotp, err := otp.NewHotp(secret, user.counter)
76 | if err != nil {
77 | fmt.Println("HOTP cannot be initialized, error: ", err)
78 | }
79 | users[i].hotp = hotp
80 | }
81 | }
82 |
83 | // The HOTP example demonstrates the following secenarios:
84 | // Adding 2 users, each with a different initial counter and
85 | // Repeat the following 10 times:
86 | // randomly select one of the users, verify that the calculated HOTP
87 | // is as expected by the provider and then increase the internal counter
88 | // of the randomly selected user by one
89 | func ExampleHotp() {
90 | users := []HotpUser{{name: "camera", counter: 2000}, {name: "cell-phone", counter: 1000}}
91 | initHotpUserslist(users, secret)
92 |
93 | for i := 0; i < 10; i++ {
94 | idx := rand.Int() % len(users)
95 | hotp := users[idx].hotp
96 | code, err := hotp.Next()
97 | if err != nil {
98 | fmt.Println("Cannot generate HOTP, error: ", err)
99 | } else {
100 | entity, _ := entityManager.GetPropertyAttachedToEntity(users[idx].name, defs.OtpPropertyName)
101 | if entity != nil {
102 | ok, err := entity.(*otp.UserInfoOtp).VerifyOtpUserCode(code, otp.HotpType)
103 | if ok {
104 | fmt.Printf("HOTP: %v, Code: %v as expected\n", users[idx], code)
105 | users[idx].counter++
106 | } else {
107 | fmt.Printf("HOTP: %v code %v is not as expected, error: %v\n", users[idx], code, err)
108 | }
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/defs/libsecurityCommon.go:
--------------------------------------------------------------------------------
1 | // Package defs : This package is responsible for the definition of all the common variables used by the libsecurity-go library
2 | package defs
3 |
4 | import (
5 | "crypto/rand"
6 | "math/big"
7 | "time"
8 |
9 | // "io/ioutil"
10 | // "testing"
11 | // logger "github.com/ibm-security-innovation/libsecurity-go/logger"
12 |
13 | ss "github.com/ibm-security-innovation/libsecurity-go/storage"
14 | )
15 |
16 | const (
17 | // RootUserName : define the root user name for the system
18 | RootUserName = "root"
19 |
20 | // AclAllEntryName : Saved name for ACL, the same as in Linux
21 | AclAllEntryName = "All"
22 |
23 | // ExtraCharStr : The only extra charcters that can be used and need to be used by passwords
24 | ExtraCharStr = "@#%^&()'-_+=;:"
25 |
26 | // SuperUserGroupName : Saved name for ACL super users group
27 | SuperUserGroupName = "Super-users"
28 | // AdminGroupName : Saved name for ACL administrators group
29 | AdminGroupName = "Admin"
30 | // UsersGroupName : Saved name for ACL users group
31 | UsersGroupName = "Users"
32 |
33 | // AmPropertyName : Saved name for the account properties
34 | AmPropertyName string = "AM"
35 | // AclPropertyName : Saved name for the ACL properties
36 | AclPropertyName string = "ACL"
37 | // OtpPropertyName : Saved name for the OTP properties
38 | OtpPropertyName string = "OTP"
39 | // OcraPropertyName : Saved name for the OCRA properties
40 | OcraPropertyName string = "OCRA"
41 | // PwdPropertyName : Saved name for the Password properties
42 | PwdPropertyName string = "PWD"
43 | // UmPropertyName : Saved name for the users/groups/resources properties
44 | UmPropertyName string = "UM"
45 |
46 | // PasswordThrottlingMiliSec : throttling delay in mili seconds when password does not match or if the entity does not exist
47 | // to handle timing atacks
48 | PasswordThrottlingMiliSec = 1000
49 | // ThrottleMaxRandomMiliSec : maximum random throttling delay in mili seconds to be added to PasswordThrottlingMiliSec in order to handle timing atacks
50 | ThrottleMaxRandomMiliSec = 10 // to avoid timimg attacks
51 | )
52 |
53 | var (
54 | // PropertiesName : which properties to store/load from secure storage
55 | PropertiesName = map[string]bool{
56 | AmPropertyName: true,
57 | AclPropertyName: true,
58 | OtpPropertyName: true,
59 | OcraPropertyName: true,
60 | PwdPropertyName: true,
61 | UmPropertyName: true,
62 | }
63 | )
64 |
65 | // Serializer : virtual set of functions that must be implemented by each module
66 | type Serializer interface {
67 | PrintProperties(data interface{}) string
68 | AddToStorage(prefix string, data interface{}, storage *ss.SecureStorage) error
69 | ReadFromStorage(prefix string, storage *ss.SecureStorage) (interface{}, error)
70 | IsEqualProperties(d1 interface{}, d2 interface{}) bool
71 | }
72 |
73 | // SerializersMap : hash structure, the key is the module property name
74 | type SerializersMap map[string]Serializer
75 |
76 | // Serializers : create the SerializersMap structure
77 | var Serializers = make(SerializersMap)
78 |
79 | // TimingAttackSleep : sleep for given delay in mili seconds plus random delay in the given rabge: to handle timing attacks
80 | func TimingAttackSleep(baseSleepMiliSec int64, maxRandomMiliSec int64) {
81 | if maxRandomMiliSec <= 0 {
82 | maxRandomMiliSec = 1
83 | }
84 | nBig, err := rand.Int(rand.Reader, big.NewInt(maxRandomMiliSec))
85 | if err != nil {
86 | nBig = big.NewInt(maxRandomMiliSec / 2)
87 | }
88 | time.Sleep(time.Duration(nBig.Int64()+baseSleepMiliSec) * time.Millisecond)
89 | }
90 |
91 | // GetBeginningOfTime : return 1/1/1970
92 | func GetBeginningOfTime() time.Time {
93 | return time.Date(1970, time.January, 1, 1, 0, 0, 0, time.Local)
94 | }
95 |
--------------------------------------------------------------------------------
/otp/otpExternal_test.go:
--------------------------------------------------------------------------------
1 | package otp
2 |
3 | import (
4 | "crypto/sha1"
5 | "encoding/base32"
6 | "strings"
7 | "testing"
8 | "time"
9 | )
10 |
11 | // Base32 as defined in RFC 4648 Base 32 alphabet
12 | // Convert a given string to Byte32 (upper case, unknown chars + 0,1,l will be converted to 'O')
13 | // The length of the string should be a multiple of 40 bites on base32
14 | // Note that the values for ilegal characters are not defined so I used "O"
15 | func ConvertToLegalBase32(str string, t *testing.T) string {
16 | base32chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
17 | pad := "O"
18 | orgLen := len(str)
19 |
20 | length := ((len(str)-1)/8 + 1) * 8
21 | t.Log("Original string:", str, ", length:", len(str), ", the new base32 len", length)
22 | for i := len(str); i < length; i++ {
23 | str = pad + str
24 | }
25 | ret := []byte(strings.ToUpper(str))
26 | for i := length - orgLen; i < len(ret); i++ {
27 | if strings.IndexByte(base32chars, ret[i]) == -1 {
28 | ret[i] = 'O'
29 | }
30 | }
31 | t.Log("Converted to base32 string", string(ret))
32 | return string(ret)
33 | }
34 |
35 | // Check the base32 conversion
36 | func Test_ChangedToLegalBase32(t *testing.T) {
37 | secrets := [][]string{{"abc", "OOOOOABC"}, {"a0123", "OOOAOO23"},
38 | {"aBn:{}()laA", "OOOOOABNOOOOOLAA"}}
39 | for _, str := range secrets {
40 | res := ConvertToLegalBase32(str[0], t)
41 | if res != str[1] {
42 | t.Error("Base32 test: Conversion of the string", str[0], "is not as expected (", str[1], ") received: ", res)
43 | } else {
44 | t.Log("Conversion of string (", str[1], ") to (", res, ") is as expected")
45 | }
46 | }
47 | }
48 |
49 | // Verify that an error is generated only for illegal secret key length
50 | func Test_OtpIllegalSecret(t *testing.T) {
51 | secret := make([]byte, maxSecretLen+10)
52 |
53 | for i := 0; i < maxSecretLen+10; i++ {
54 | _, err := NewOtp(secret[:i])
55 | if err == nil && (i < minSecretLen || i > maxSecretLen) {
56 | t.Errorf("Test failed: initialization was done for a secret key with an illegal length %d. Valid lengths are in the range %d-%d", i, minSecretLen, maxSecretLen)
57 | } else if err != nil && (i >= minSecretLen && i <= maxSecretLen) {
58 | t.Errorf("Test failed: Otp initialization failed for a secret key with a legal length %d, error: %v", i, err)
59 | }
60 | }
61 | }
62 |
63 | // Test sha1 6 digits with varius secret keys against the Google authenticator calculations
64 | // The input string is converted into a valid base32 string (without undefined characters and with the appropriate length)
65 | func Test_OtpTestTotpVsGoogleAuthenticator(t *testing.T) {
66 | var val, ref, secret string
67 | b32secret := [][]string{{"JBSWY3DPEHPK3PXP", "JBSWY3DPEHPK3PXP"},
68 | {"ABCD2345", "ABCD2345"}, {"a123", "OOOOAO23"},
69 | {"abcA(){}AA", "OOOOOOABCAOOOOAA"}}
70 | totp, err := NewTotp(BaseSecret)
71 | if err != nil {
72 | t.Error("Test fail,", err)
73 | t.FailNow()
74 | }
75 |
76 | totp.BaseOtp.digest = sha1.New
77 | totp.BaseOtp.Digits = 6
78 | totp.Interval = time.Second * 30
79 |
80 | for _, s := range b32secret {
81 | secret = ConvertToLegalBase32(s[0], t)
82 | if secret != s[1] {
83 | t.Error("Base32 test: Conversion of the string", s[0], "is not as expected (", s[1], ") received: ", secret)
84 | continue
85 | }
86 | totp.BaseOtp.Secret, _ = base32.StdEncoding.DecodeString(secret)
87 | val, err = totp.Now()
88 | if err != nil {
89 | t.Error("Test fail before running, illigal parameters:", err)
90 | continue
91 | }
92 | ref = jsOtp(secret, (time.Now().UnixNano()+500000000.0)/1000000000.0)
93 | if val != ref {
94 | t.Error("Error: internal OTP value:", val, "is different from the external OTP value :", ref,
95 | "for the same data: sha1, #of digits", totp.BaseOtp.Digits, "secret", secret)
96 | } else {
97 | t.Log("The same internal and external OTP calculation using secret:", secret)
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/acl/aclEntry_test.go:
--------------------------------------------------------------------------------
1 | package acl
2 |
3 | import (
4 | "testing"
5 |
6 | en "github.com/ibm-security-innovation/libsecurity-go/entity"
7 | )
8 |
9 | const (
10 | PerRead = "Read"
11 | PerWrite = "Write"
12 | PerExe = "Execute"
13 | PerTake = "Take"
14 | PerAll = "Can be used by All"
15 | PerNotAnEntry1 = "NotAnEntry1"
16 | PerNotAnEntry2 = "NotAnEntry2"
17 |
18 | entryName = "test1"
19 | )
20 |
21 | var permissionsVec = []en.Permission{PerRead, PerWrite, PerExe, PerTake, PerAll}
22 | var notPermissionsVec = []en.Permission{PerNotAnEntry1, "", PerNotAnEntry2}
23 | var permissionsMap = make(map[en.Permission]bool)
24 |
25 | func init() {
26 | for _, p := range permissionsVec {
27 | permissionsMap[p] = true
28 | }
29 | for _, p := range notPermissionsVec {
30 | permissionsMap[p] = false
31 | }
32 | }
33 |
34 | func addPermissions(a *Entry, permissions map[en.Permission]bool, expected bool) (bool, *en.Permission) {
35 | for p, val := range permissions {
36 | if val == true {
37 | val, err := a.AddPermission(p)
38 | if len(p) == 0 && err != nil {
39 | return !expected, &p
40 | }
41 | if val != expected {
42 | return false, &p
43 | }
44 | }
45 | }
46 | return true, nil
47 | }
48 |
49 | // Verify that empty permission can't be added
50 | // verify that valid permission can be added
51 | // verify that permission that is in the list can't be added again
52 | // verify that only permission that are in the list can be removed
53 | // Verify that at the end of the loop, no permissions are left in the list
54 | func Test_AddRemovePermissions(t *testing.T) {
55 | expected := []bool{true, false}
56 | name := "User1"
57 |
58 | a, _ := NewEntry(name)
59 | // verify that empty permission can't be added
60 | p := en.Permission("")
61 | _, err := a.AddPermission(p)
62 | if err == nil {
63 | t.Errorf("Test fail: Invalid permission: '%v' was added to the %v", p, a)
64 | }
65 | // verify that valid permission can be added
66 | // verify that permission that is in the list can't be added again
67 | for _, exp := range expected {
68 | ok, permission := addPermissions(a, permissionsMap, exp)
69 | if ok == false {
70 | if exp == true {
71 | t.Errorf("Test fail: Fail to add valid permission: '%v' to %v", permission, a)
72 | } else {
73 | t.Errorf("Test fail: Permission: '%v' was added to %v but is was already added before", permission, a)
74 | }
75 | }
76 | }
77 |
78 | // verify that only permission that are in the list can be removed
79 | for p, val := range permissionsMap {
80 | err := a.RemovePermission(p)
81 | if err != nil && val == true {
82 | t.Errorf("Test fail: Fail to remove valid permission: '%v' from %v", p, a)
83 | } else if err == nil && val == false {
84 | t.Errorf("Test fail: Permission: '%v' was removed from %v, but it wasn't in the permissions list", p, a)
85 | }
86 | }
87 | if len(a.Permissions) != 0 {
88 | t.Error("Test fail: The permission list of", a, "must be empty")
89 | }
90 | }
91 |
92 | // Add permissions to an entry and verify that only permission that are in the
93 | // entry return true
94 | func Test_CheckPermissions(t *testing.T) {
95 | name := "User1"
96 | //
97 | // ul := um.NewUsersList()
98 | // user1, _ := um.NewUser(name, nil)
99 | // ul.AddUser(user1)
100 | a, _ := NewEntry(name)
101 |
102 | // verify that valid permission can be added
103 | ok, permission := addPermissions(a, permissionsMap, true)
104 | if ok == false {
105 | t.Errorf("Test fail: Fail to add valid permission: '%v' to %v", permission, a)
106 | }
107 | // verify that only permission that are in the list returns true
108 | for p, val := range permissionsMap {
109 | ok, _ := a.CheckPermission(p)
110 | if ok == false && val == true {
111 | t.Errorf("Test fail: Permission: '%v' from %v wasn't found", p, a)
112 | } else if ok == true && val == false {
113 | t.Errorf("Test fail: Permission: '%v' that is not in %v, was found", p, a)
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/swagger-dist/lib/jquery.ba-bbq.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
3 | * http://benalman.com/projects/jquery-bbq-plugin/
4 | *
5 | * Copyright (c) 2010 "Cowboy" Ben Alman
6 | * Dual licensed under the MIT and GPL licenses.
7 | * http://benalman.com/about/license/
8 | */
9 | (function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this);
--------------------------------------------------------------------------------
/restful/otp-restful/otp_command.go:
--------------------------------------------------------------------------------
1 | package otpRestful
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/emicklei/go-restful"
7 | "github.com/ibm-security-innovation/libsecurity-go/otp"
8 | cr "github.com/ibm-security-innovation/libsecurity-go/restful/common-restful"
9 | )
10 |
11 | const (
12 | handleUserCommand = iota
13 | handleUserBlockCommand
14 | verifyUserCodeCommand
15 | )
16 |
17 | var (
18 | commandsToPath = []cr.ComamndsToPath{
19 | {handleUserCommand, "%v/{%v}"},
20 | {handleUserBlockCommand, "%v/{%v}/%v"},
21 | {verifyUserCodeCommand, "%v/{%v}/%v"},
22 | }
23 |
24 | urlCommands = make(cr.CommandToPath)
25 | )
26 |
27 | func initCommandToPath() {
28 | for _, c := range commandsToPath {
29 | urlCommands[c.Command] = c.Path
30 | }
31 | }
32 |
33 | func (u OtpRestful) setRoute(service *restful.WebService) {
34 | str := fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
35 | service.Route(service.PUT(str).
36 | Filter(u.st.SuperUserFilter).
37 | To(u.restAddOtp).
38 | Doc("Add OTP").
39 | Operation("addOtp").
40 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
41 | Reads(cr.Secret{}).
42 | Writes(cr.URL{}))
43 |
44 | str = fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
45 | service.Route(service.GET(str).
46 | Filter(u.st.SameUserFilter).
47 | To(u.restGetOtp).
48 | Doc("Get the OTP").
49 | Operation("getOtp").
50 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
51 | Writes(otp.UserInfoOtp{}))
52 |
53 | str = fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
54 | service.Route(service.DELETE(str).
55 | Filter(u.st.SuperUserFilter).
56 | To(u.restDeleteOtp).
57 | Doc("Remove the OTP").
58 | Operation("deleteOTP").
59 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")))
60 |
61 | str = fmt.Sprintf(urlCommands[handleUserBlockCommand], usersPath, userIDParam, blockedStateToken)
62 | service.Route(service.GET(str).
63 | Filter(u.st.SameUserFilter).
64 | To(u.restIsOtpBlocked).
65 | Doc("Check if OTP is blocked").
66 | Operation("isOtpBlocked").
67 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
68 | Writes(userState{}))
69 |
70 | str = fmt.Sprintf(urlCommands[handleUserBlockCommand], usersPath, userIDParam, blockedStateToken)
71 | service.Route(service.PUT(str).
72 | Filter(u.st.SuperUserFilter).
73 | To(u.restSetOtpBlockedState).
74 | Doc("Set the OTP blocked state").
75 | Operation("setOtpBlockedState").
76 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
77 | Reads(userState{}).
78 | Writes(cr.URL{}))
79 |
80 | str = fmt.Sprintf(urlCommands[verifyUserCodeCommand], usersPath, userIDParam, verifyHotpTypeParam)
81 | service.Route(service.POST(str).
82 | To(u.restVerifyOtpHotpUserCode). // no filter is needed
83 | Doc("Verify that a given OTP is as expected, counter base").
84 | Operation("verifyHotpUserCode").
85 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
86 | Reads(cr.Secret{}).
87 | Writes(cr.Match{}))
88 |
89 | str = fmt.Sprintf(urlCommands[verifyUserCodeCommand], usersPath, userIDParam, verifyTotpTypeParam)
90 | service.Route(service.POST(str).
91 | To(u.restVerifyOtpTotpUserCode). // no filter is needed
92 | Doc("Verify that a given code is as expected, time base").
93 | Operation("verifyTotpUserCode").
94 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
95 | Reads(cr.Secret{}).
96 | Writes(cr.Match{}))
97 | }
98 |
99 | // RegisterBasic : register the OTP to the RESTFul API container
100 | func (u OtpRestful) RegisterBasic(container *restful.Container) {
101 | servicePath = cr.ServicePathPrefix + cr.Version + otpPrefix
102 |
103 | service := new(restful.WebService)
104 | service.
105 | Path(servicePath).
106 | Consumes(restful.MIME_JSON).
107 | Produces(restful.MIME_JSON)
108 | //.Doc("One Time Password")
109 |
110 | u.setRoute(service)
111 | container.Add(service)
112 | }
113 |
--------------------------------------------------------------------------------
/restful/storage-restful/secureStorage_command.go:
--------------------------------------------------------------------------------
1 | package storageRestful
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/emicklei/go-restful"
7 | "github.com/ibm-security-innovation/libsecurity-go/restful/common-restful"
8 | )
9 |
10 | const (
11 | handleStorageCommand = iota
12 | )
13 |
14 | type commandToPath map[int]string
15 |
16 | var (
17 | commandsToPath = []comamndsToPathS{
18 | {handleStorageCommand, "%v"},
19 | }
20 | urlCommands = make(commandToPath)
21 | // old use AmRestful = accountsRestful.NewAmRestful()
22 | )
23 |
24 | type comamndsToPathS struct {
25 | command int
26 | path string
27 | }
28 |
29 | func initCommandToPath() {
30 | for _, c := range commandsToPath {
31 | urlCommands[c.command] = c.path
32 | }
33 | }
34 |
35 | func (s SRestful) setRoute(service *restful.WebService) {
36 | str := fmt.Sprintf(urlCommands[handleStorageCommand], storagePath)
37 | service.Route(service.PUT(str).
38 | // no filter is needed, the filter is the secure key Filter(s.st.SuperUserFilter).
39 | To(s.restCreateSecureStorage).
40 | Doc("Create a new secure storage").
41 | Operation("CreateSecureStorage").
42 | Param(service.HeaderParameter(secretIDParam, secretComment).DataType("string")).
43 | Writes(commonRestful.URL{}))
44 |
45 | str = fmt.Sprintf(urlCommands[handleStorageCommand], storagePath)
46 | service.Route(service.DELETE(str).
47 | // no filter is needed, the filter is the secure key Filter(s.st.SuperUserFilter).
48 | To(s.restDeleteSecureStorage).
49 | Doc("Remove the current secure storage").
50 | Param(service.HeaderParameter(secretIDParam, secretComment).DataType("string")).
51 | Operation("RemoveSecureStorage"))
52 |
53 | str = fmt.Sprintf(urlCommands[handleStorageCommand], storagePath)
54 | service.Route(service.GET(str).
55 | // no filter is needed, the filter is the secure key Filter(s.st.SuperUserFilter).
56 | To(s.restGetSecureStorage).
57 | Doc("Get a secure storage").
58 | Operation("getSecureStorage").
59 | Param(service.HeaderParameter(secretIDParam, secretComment).DataType("string")))
60 |
61 | str = fmt.Sprintf(urlCommands[handleStorageCommand], storageItemPath)
62 | service.Route(service.PATCH(str).
63 | // no filter is needed, the filter is the secure key Filter(s.st.SuperUserFilter).
64 | To(s.restAddItemToSecureStorage).
65 | Doc("Add a new item to the secure storage").
66 | Operation("addANewItemToTheSecureStorage").
67 | Param(service.HeaderParameter(secretIDParam, secretComment).DataType("string")).
68 | Reads(itemData{}).
69 | Writes(commonRestful.URL{}))
70 |
71 | str = fmt.Sprintf(urlCommands[handleStorageCommand], storageItemPath)
72 | service.Route(service.GET(str).
73 | // no filter is needed, the filter is the secure key Filter(s.st.SuperUserFilter).
74 | To(s.restGetItemFromSecureStorage).
75 | Doc("Get an item from the secure storage").
76 | Operation("getAnItemFromTheSecureStorage").
77 | Param(service.HeaderParameter(secretIDParam, secretComment).DataType("string")).
78 | Param(service.HeaderParameter(keyIDParam, keyComment).DataType("string")).
79 | Writes(itemValue{}))
80 |
81 | str = fmt.Sprintf(urlCommands[handleStorageCommand], storageItemPath)
82 | service.Route(service.DELETE(str).
83 | // no filter is needed, the filter is the secure key Filter(s.st.SuperUserFilter).
84 | To(s.restDeleteItemFromSecureStorage).
85 | Doc("Delete item from a secure storage").
86 | Operation("deleteItemFromSecureStorage").
87 | Param(service.HeaderParameter(secretIDParam, secretComment).DataType("string")).
88 | Param(service.HeaderParameter(keyIDParam, keyComment).DataType("string")))
89 | }
90 |
91 | // RegisterBasic : register the Secure Storage to the RESTFul API container
92 | func (s SRestful) RegisterBasic(container *restful.Container) {
93 | servicePath = commonRestful.ServicePathPrefix + commonRestful.Version + sPrefix
94 |
95 | service := new(restful.WebService)
96 | service.
97 | Path(servicePath).
98 | Consumes(restful.MIME_JSON).
99 | Produces(restful.MIME_JSON).
100 | Doc("Secure Storage")
101 |
102 | s.setRoute(service)
103 | container.Add(service)
104 | }
105 |
--------------------------------------------------------------------------------
/swagger-dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | IoT API
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
83 |
84 |
85 |
86 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/otp/otp_test.go:
--------------------------------------------------------------------------------
1 | package otp
2 |
3 | import (
4 | "crypto/md5"
5 | "crypto/sha1"
6 | "crypto/sha256"
7 | "fmt"
8 | "hash"
9 | "time"
10 | )
11 |
12 | // Following tests are required (all are implemented here):
13 | // 1. Basic functionality:
14 | // 1.1 Unit tests:
15 | // 1.1.1 Verify that empty secret keys cause an error
16 | // 1.1.2 Verify the validity of the base32 conversion function
17 | // 1.1.3 Verify that the next() function for HOTP works properly
18 | // 1.1.4 The next() and new() functions for event based OPT generate diferent OPTs
19 | // 1.2 Results accuracy
20 | // 1.2.1 Different parameters[input values?] result with different generated OTPs
21 | // 1.2.2 Repetition: Verify that identical input parameters the same parameters result with identical OTPs (TOTP and HOTP)
22 | // 1.2.3 Time manipulations:
23 | // 1.2.3.1 increments within the defined lifespan of a TOTP result with identical OTPs
24 | // 1.2.3.2 Time increments larger than the defined lifespan of a TOTP result with different OTPs
25 | // 2. External validation:
26 | // 2.1 Test that the OTP results match external code results
27 | // May need to add:
28 | // 1. System check: Client provider
29 | // 2. Resync message in the event based HMAC OTP
30 |
31 | var BaseSecret = []byte("Aa@bCDEFGH12345678")
32 |
33 | type TotpRun struct {
34 | totp *Totp
35 | at time.Time
36 | result string
37 | }
38 |
39 | func (t TotpRun) String() string {
40 | ret := fmt.Sprintf("Data: secret key = %v, digit = %v, digest = %v, interval = %v",
41 | t.totp.BaseOtp.Secret, t.totp.BaseOtp.Digits, t.totp.BaseOtp.digest, t.totp.Interval)
42 | return ret
43 | }
44 |
45 | type HotpRun struct {
46 | hotp *Hotp
47 | counter int64
48 | result string
49 | }
50 |
51 | func (t HotpRun) String() string {
52 | ret := fmt.Sprintf("Data: secret key = %v, digit = %v, digest = %v, counter = %v",
53 | t.hotp.BaseOtp.Secret, t.hotp.BaseOtp.Digits, t.hotp.BaseOtp.digest, t.hotp.Count)
54 | return ret
55 | }
56 |
57 | var hashes = []func() hash.Hash{sha1.New, sha256.New, md5.New}
58 | var secrets = []string{"ABCDABCD11112222", "AC234123456789012", "ABCDEFG12345691234"}
59 | var digits = []int{6, 7, 8}
60 | var interval = []time.Duration{30 * time.Second, 60 * time.Second}
61 | var startCounter = []int64{0, 12345}
62 |
63 | var referenceRunsTotp []TotpRun
64 | var referenceRunsHotp []HotpRun
65 |
66 | // initialize the refference structures with setup and expected results
67 | func init() {
68 | referenceRunsTotp = createReferenceRunsTotp()
69 | referenceRunsHotp = createReferenceRunsHotp()
70 | }
71 |
72 | func createReferenceRunsTotp() []TotpRun {
73 | var refRunsTotp []TotpRun
74 | at := time.Now()
75 | var useTime time.Time
76 |
77 | for _, h := range hashes {
78 | for _, s := range secrets {
79 | for _, d := range digits {
80 | for _, in := range interval {
81 | useTime = at.Round(in)
82 | totp, err := NewTotp([]byte(s))
83 | if err != nil {
84 | fmt.Println("Error while initializing")
85 | panic(err)
86 | }
87 | totp.Interval = in
88 | totp.BaseOtp.digest = h
89 | totp.BaseOtp.Digits = d
90 | res, err := totp.AtTime(useTime)
91 | if err != nil {
92 | fmt.Println("Error while initializing")
93 | panic(err)
94 | }
95 | ref := TotpRun{totp, useTime, res}
96 | refRunsTotp = append(refRunsTotp, ref)
97 | }
98 | }
99 | }
100 | }
101 | return refRunsTotp
102 | }
103 |
104 | func createReferenceRunsHotp() []HotpRun {
105 | var refRunsHotp []HotpRun
106 |
107 | for _, h := range hashes {
108 | for _, s := range secrets {
109 | for _, d := range digits {
110 | for _, c := range startCounter {
111 | hotp, err := NewHotp([]byte(s), c)
112 | if err != nil {
113 | fmt.Println("Error while initializing")
114 | panic(err)
115 | }
116 | hotp.BaseOtp.digest = h
117 | hotp.BaseOtp.Digits = d
118 | res, err := hotp.AtCount(c)
119 | if err != nil {
120 | fmt.Println("Error while initializing")
121 | panic(err)
122 | }
123 | ref := HotpRun{hotp, c, res}
124 | refRunsHotp = append(refRunsHotp, ref)
125 | }
126 | }
127 | }
128 | }
129 | return refRunsHotp
130 | }
131 |
--------------------------------------------------------------------------------
/setup/setup_storage_file.go:
--------------------------------------------------------------------------------
1 | // Initialization services.
2 | //
3 | // Utility that generates an initial secureStorage file to be used later by all other components
4 | // The usage is:
5 | // usage: generate_login_file
6 | // -generate-rsa=false: Generate RSA private/public files ('key.private', 'key.pub')
7 | // -login-file="./data.txt": First data file that includes the root user
8 | // -password="root": Root password
9 | // -secure-key="./secureKey": secure key file path
10 | package main
11 |
12 | import (
13 | "crypto/rand"
14 | "crypto/rsa"
15 | "crypto/x509"
16 | "encoding/pem"
17 | "flag"
18 | "fmt"
19 | "io/ioutil"
20 | "log"
21 | "os"
22 | "path/filepath"
23 |
24 | am "github.com/ibm-security-innovation/libsecurity-go/accounts"
25 | defs "github.com/ibm-security-innovation/libsecurity-go/defs"
26 | en "github.com/ibm-security-innovation/libsecurity-go/entity"
27 | "github.com/ibm-security-innovation/libsecurity-go/password"
28 | "github.com/ibm-security-innovation/libsecurity-go/salt"
29 | ss "github.com/ibm-security-innovation/libsecurity-go/storage"
30 | )
31 |
32 | const (
33 | saltLen = 8
34 |
35 | rsaPrivateKeyFileName = "key.private"
36 | rsaPublicKeyFileName = "key.pub"
37 | )
38 |
39 | var ()
40 |
41 | func init() {
42 | }
43 |
44 | func usage() {
45 | _, file := filepath.Split(os.Args[0])
46 | fmt.Fprintf(os.Stderr, "usage: %v.go\n", file)
47 | flag.PrintDefaults()
48 | os.Exit(2)
49 | }
50 |
51 | // Generate a new secure storage minimal file that includes the root user with
52 | // basic Account Management: the root user privilege and password
53 | func createBasicFile(stFilePath string, name string, pass string, key []byte) {
54 | saltStr, _ := salt.GetRandomSalt(saltLen)
55 | _, err := salt.GenerateSaltedPassword([]byte(pass), password.MinPasswordLength, password.MaxPasswordLength, saltStr, -1)
56 | if err != nil {
57 | log.Fatalf("Error: can't generate salted password for '%v' user, error: %v", name, err)
58 | }
59 | ul := en.New()
60 | ul.AddUser(name)
61 | amUser, _ := am.NewUserAm(am.SuperUserPermission, []byte(pass), saltStr, true)
62 | ul.AddPropertyToEntity(name, defs.AmPropertyName, amUser)
63 | ul.StoreInfo(stFilePath, key, false)
64 | }
65 |
66 | // Generate RSA public and private keys to the given file name
67 | func generateRSAKeys(rsaPrivateKeyFileName string, rsaPublicKeyFileName string) {
68 | privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
69 | if err != nil {
70 | log.Fatalf("Error: can't generate rsa key, error: %v", err)
71 | }
72 | privateASN1 := x509.MarshalPKCS1PrivateKey(privateKey)
73 | privateBytes := pem.EncodeToMemory(&pem.Block{
74 | Type: "RSA PRIVATE KEY",
75 | Bytes: privateASN1,
76 | })
77 | ioutil.WriteFile(rsaPrivateKeyFileName, privateBytes, 0644)
78 |
79 | pubASN1, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
80 | if err != nil {
81 | log.Fatalf("Error: can't generate rsa public key from the private key, error: %v", err)
82 | }
83 | pubBytes := pem.EncodeToMemory(&pem.Block{
84 | Type: "RSA PUBLIC KEY",
85 | Bytes: pubASN1,
86 | })
87 | ioutil.WriteFile(rsaPublicKeyFileName, pubBytes, 0644)
88 | fmt.Println("Generate RSA files:", rsaPrivateKeyFileName, "And", rsaPublicKeyFileName)
89 | }
90 |
91 | func main() {
92 | defaultRootPassword := defs.RootUserName
93 |
94 | secureKeyFileNamePath := flag.String("secure-key", "./secureKey", "secure key file path")
95 | loginFilePath := flag.String("storage-file", "./data.txt", "First storage file that includes the root user")
96 | rootPassword := flag.String("password", defaultRootPassword, "Root password")
97 | str := fmt.Sprintf("Generate RSA private/public files ('%s', '%s')", rsaPrivateKeyFileName, rsaPublicKeyFileName)
98 | generateRSA := flag.Bool("generate-rsa", false, str)
99 | flag.Parse()
100 | if flag.NArg() > 0 {
101 | usage()
102 | }
103 |
104 | if *rootPassword == defaultRootPassword {
105 | fmt.Printf("Error: The root password must be set (and not to '%v')\n", defaultRootPassword)
106 | usage()
107 | }
108 | err := password.CheckPasswordStrength(*rootPassword)
109 | if err != nil {
110 | log.Fatalf("Error: The root password must be more complex: %v", err)
111 | }
112 |
113 | key := ss.GetSecureKey(*secureKeyFileNamePath)
114 | createBasicFile(*loginFilePath, defs.RootUserName, *rootPassword, key)
115 | fmt.Println("The generated file name is:", *loginFilePath)
116 | if *generateRSA {
117 | generateRSAKeys(rsaPrivateKeyFileName, rsaPublicKeyFileName)
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/otp/otpHotp_test.go:
--------------------------------------------------------------------------------
1 | package otp
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func setStateHotp(ot *Hotp, state HotpRun) {
8 | ot.BaseOtp.Secret = state.hotp.BaseOtp.Secret
9 | ot.BaseOtp.digest = state.hotp.BaseOtp.digest
10 | ot.BaseOtp.Digits = state.hotp.BaseOtp.Digits
11 | ot.Count = state.hotp.Count
12 | }
13 |
14 | func testGetHotp(t *testing.T) *Hotp {
15 | hotp, err := NewHotp(BaseSecret, defaultStartCounter)
16 | if err != nil {
17 | t.Error("Test fail,", err)
18 | t.FailNow()
19 | }
20 | return hotp
21 | }
22 |
23 | // Verify that different counter values with the same counter seed as well as identical counter values with different counter seeds result with different OTPs and
24 | // identical counter values with identical counter seeds result with identical OTPs
25 | func Test_OtpTestHotpAtCount(t *testing.T) {
26 | hotp := testGetHotp(t)
27 | counterOffset := []int64{0, -1, 1, 12345}
28 |
29 | for _, offset := range counterOffset {
30 | for _, data := range referenceRunsHotp {
31 | setStateHotp(hotp, data)
32 | val, err := data.hotp.AtCount(data.counter + offset)
33 | if err != nil {
34 | t.Error("Test fail before running, illigal parameters:", err)
35 | continue
36 | }
37 | if offset == 0 {
38 | if val != data.result {
39 | t.Error("OTP did not change after incrementing the counter even though all other parameters, including the seed counter, are identical:", data,
40 | "original seed -", data.counter, ", curent seed -", data.counter+offset,
41 | "but the OTP -", data.result, "is different from the resultant OPT -", val)
42 | } else {
43 | t.Log("OTP calculated value for the same seed stays the same (as expected)")
44 | }
45 |
46 | }
47 | if offset != 0 {
48 | if val == data.result {
49 | t.Error("OTP value did not change when the seed counter changed (all other parameters remained identical) :", data,
50 | "original seed -", data.counter, ", curent seed -", data.counter+offset,
51 | "but the OTP calculated value was the same", val)
52 | } else {
53 | t.Log("OTP calculated value for different seed changed (as expected)")
54 | }
55 | }
56 | }
57 | }
58 | }
59 |
60 | // If all parameters are the same, the resultant OTP should be repetative
61 | // If the OTP request start counter was changed (either a different counter or the use use of .Next() or .New()) and
62 | // all other parameters are the same, the resultant OTP should be different
63 | func Test_OtpTestHotpNextNew(t *testing.T) {
64 | hotp := testGetHotp(t)
65 | funcCall := [](func() (string, error)){hotp.New, hotp.Next}
66 |
67 | for i, f := range funcCall {
68 | for _, data := range referenceRunsHotp {
69 | setStateHotp(hotp, data)
70 | val, err := f()
71 | if val == data.result {
72 | val, err = f() // give it another chance
73 | }
74 | if err != nil {
75 | t.Error("Test fail before running, illigal parameters:", err)
76 | continue
77 | }
78 | if val == data.result {
79 | t.Error("OTP value did not change when the seed counter changed",
80 | "(all other parameters remained identical). Using function:", i, "(0: .New(), 1: .Next())) must return different OTP values:", data,
81 | "but the OTP calculated value was the same", val)
82 | } else {
83 | // t.Log("OTP calculated value for different seed due to .next/.new call was changed (as expected)")
84 | }
85 | }
86 | }
87 | }
88 |
89 | // WIll check a loop of 11, from a referance counter of -5 to +5
90 | // The only time it should get the same results is when the counters are the same
91 | func Test_OtpTestHotpAccuracy(t *testing.T) {
92 | hotp := testGetHotp(t)
93 | shift := int64(5)
94 |
95 | for _, data := range referenceRunsHotp {
96 | data.hotp.Count = data.hotp.Count - shift - 1
97 | setStateHotp(hotp, data)
98 | for i := -shift; i < shift+1; i++ {
99 | val, err := data.hotp.Next()
100 | if err != nil {
101 | t.Error("Test fail before running, illigal parameters:", err)
102 | break
103 | }
104 | if i != 0 && val == data.result {
105 | t.Error("OTP value did not change when the seed counter changed",
106 | "(all other parameters remained identical) :", data,
107 | "but the OTP calculated value was the same:", val)
108 | }
109 | if i == 0 && val != data.result {
110 | t.Error("OTP value modified even though all parameters",
111 | "as well as the counter value and seed counter are identical: ", data,
112 | "OTP", val, "is different from the result OTP:", data.result)
113 | }
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/restful/libsecurity-restful/libsecurity_test.go:
--------------------------------------------------------------------------------
1 | package libsecurityRestful
2 |
3 | import (
4 | "testing"
5 |
6 | am "github.com/ibm-security-innovation/libsecurity-go/accounts"
7 | "github.com/ibm-security-innovation/libsecurity-go/acl"
8 | defs "github.com/ibm-security-innovation/libsecurity-go/defs"
9 | en "github.com/ibm-security-innovation/libsecurity-go/entity"
10 | "github.com/ibm-security-innovation/libsecurity-go/ocra"
11 | "github.com/ibm-security-innovation/libsecurity-go/otp"
12 | "github.com/ibm-security-innovation/libsecurity-go/password"
13 | )
14 |
15 | const ()
16 |
17 | var (
18 | St *LibsecurityRestful
19 |
20 | secret = []byte("ABCDEFGH12345678")
21 | salt = []byte("Salt")
22 | )
23 |
24 | func init() {
25 | St = NewLibsecurityRestful()
26 | }
27 |
28 | // Verify that get property from undefined user returns an error
29 | // Verify that get property from user before setting the OTP property, returns an error
30 | // Verify that get property from user after setting the property returns the same property as was setted to the user
31 | // Verify that get property from user after removing the OTP property returns an error
32 | // Verify that get property from user after readding the OTP property returns OK
33 | // Verify that get property from user that was removed after OTP property was set, returns an error
34 | // Verify that Add a property to user, remove the user, generate a new user with the same name and try to get the property returns an error
35 | func testAddCheckRemoveUserProperty(t *testing.T, propertyName string, moduleData interface{}) {
36 | name := "name1"
37 | usersList := en.New()
38 | _, err := usersList.GetPropertyAttachedToEntity(name, propertyName)
39 | if err == nil {
40 | t.Errorf("Test fail, Recived module '%v' of undefined user '%v'", propertyName, name)
41 | }
42 |
43 | usersList.AddResource(name)
44 | _, err = usersList.GetPropertyAttachedToEntity(name, propertyName)
45 | if err == nil {
46 | t.Errorf("Test fail, Recived module '%v' of not registered yet module for user '%v'", propertyName, name)
47 | }
48 |
49 | usersList.AddPropertyToEntity(name, propertyName, moduleData)
50 | tmp, err := usersList.GetPropertyAttachedToEntity(name, propertyName)
51 | if err != nil {
52 | t.Errorf("Test fail, Error while feteching module '%v' from user '%v', error: %v", propertyName, name, err)
53 | }
54 | if moduleData != tmp {
55 | t.Errorf("Test fail, Added '%v' property '%v' is not equal to the fetched one '%v'", propertyName, moduleData, tmp)
56 | }
57 |
58 | usersList.RemovePropertyFromEntity(name, propertyName)
59 | _, err = usersList.GetPropertyAttachedToEntity(name, propertyName)
60 | if err == nil {
61 | t.Errorf("Test fail, Removed module '%v' from user '%v' was successfully fetched", propertyName, name)
62 | }
63 |
64 | usersList.AddPropertyToEntity(name, propertyName, moduleData)
65 | _, err = usersList.GetPropertyAttachedToEntity(name, propertyName)
66 | if err != nil {
67 | t.Errorf("Test fail, Error while feteching module '%v' from user '%v', error: %v", propertyName, name, err)
68 | }
69 |
70 | usersList.RemoveResource(name)
71 | _, err = usersList.GetPropertyAttachedToEntity(name, propertyName)
72 | if err == nil {
73 | t.Errorf("Test fail, Module '%v' of removed user '%v' was successfully fetched", propertyName, name)
74 | }
75 | err = usersList.AddPropertyToEntity(name, propertyName, moduleData)
76 | if err == nil {
77 | t.Errorf("Test fail, Atteched module '%v' to removed user '%v'", propertyName, name)
78 | }
79 | usersList.AddResource(name)
80 | _, err = usersList.GetPropertyAttachedToEntity(name, propertyName)
81 | if err == nil {
82 | t.Errorf("Test fail, Module '%v' was fetched before atttached to the user '%v'", propertyName, name)
83 | }
84 | }
85 |
86 | func Test_AddCheckRemoveOtpUserProperty(t *testing.T) {
87 | moduleData, _ := otp.NewSimpleOtpUser(secret, false)
88 |
89 | testAddCheckRemoveUserProperty(t, defs.OtpPropertyName, moduleData)
90 | }
91 |
92 | func Test_AddCheckRemovePwdUserProperty(t *testing.T) {
93 | moduleData, _ := password.NewUserPwd(secret, salt, false)
94 |
95 | testAddCheckRemoveUserProperty(t, defs.PwdPropertyName, moduleData)
96 | }
97 |
98 | func Test_AddCheckRemoveOcraUserProperty(t *testing.T) {
99 | moduleData, _ := ocra.NewOcraUser([]byte("12345678"), "OCRA-1:HOTP-SHA512-8:C-QH08-T1M-S064-PSHA256")
100 |
101 | testAddCheckRemoveUserProperty(t, defs.OcraPropertyName, moduleData)
102 | }
103 |
104 | func Test_AddCheckRemoveAMUserProperty(t *testing.T) {
105 | moduleData, _ := am.NewUserAm(am.SuperUserPermission, secret, salt, false)
106 |
107 | testAddCheckRemoveUserProperty(t, defs.AmPropertyName, moduleData)
108 | }
109 |
110 | func Test_AddCheckRemoveACLUserProperty(t *testing.T) {
111 | moduleData := acl.NewACL()
112 |
113 | testAddCheckRemoveUserProperty(t, defs.AclPropertyName, moduleData)
114 | }
115 |
--------------------------------------------------------------------------------
/restful/accounts-restful/am_command.go:
--------------------------------------------------------------------------------
1 | package accountsRestful
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/emicklei/go-restful"
7 | am "github.com/ibm-security-innovation/libsecurity-go/accounts"
8 | cr "github.com/ibm-security-innovation/libsecurity-go/restful/common-restful"
9 | )
10 |
11 | const (
12 | handleAuthenticateCommand = iota
13 | handleUserCommand
14 | handleUserPwdCommand
15 | )
16 |
17 | var (
18 | commandsToPath = []cr.ComamndsToPath{
19 | {handleAuthenticateCommand, "%v"},
20 | {handleUserCommand, "%v/{%v}"},
21 | {handleUserPwdCommand, "%v/{%v}/%v"},
22 | }
23 |
24 | urlCommands = make(cr.CommandToPath)
25 | )
26 |
27 | func initCommandToPath() {
28 | for _, c := range commandsToPath {
29 | urlCommands[c.Command] = c.Path
30 | }
31 | }
32 |
33 | func (l AmRestful) setAmRoute(service *restful.WebService) {
34 | str := fmt.Sprintf(urlCommands[handleAuthenticateCommand], userPath)
35 | service.Route(service.PUT(str).
36 | To(l.restAm).
37 | Doc("Authenticate a user").
38 | Operation("authenticate").
39 | Reads(pUserData{}))
40 |
41 | str = fmt.Sprintf(urlCommands[handleAuthenticateCommand], logoutPath)
42 | service.Route(service.DELETE(str).To(l.restLogout).
43 | Doc("Logout the current user").
44 | Operation("logout"))
45 | }
46 |
47 | func (l AmRestful) setFullRoute(service *restful.WebService) {
48 | str := fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
49 | service.Route(service.PUT(str).
50 | Filter(l.st.SuperUserFilter).
51 | To(l.restAddAm).
52 | Doc("Add Account Management").
53 | Operation("AddNewAm").
54 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
55 | Reads(privilegePwd{}).
56 | Writes(cr.URL{}))
57 |
58 | str = fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
59 | service.Route(service.GET(str).
60 | Filter(l.st.SameUserFilter).
61 | To(l.restGetAm).
62 | Doc("Get Account Management").
63 | Operation("getAm").
64 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
65 | Writes(am.AmUserInfo{}))
66 |
67 | str = fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
68 | service.Route(service.DELETE(str).
69 | Filter(l.st.SuperUserFilter).
70 | To(l.restDeleteAM).
71 | Doc("Remove Account Management").
72 | Operation("deleteAm").
73 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")))
74 |
75 | str = fmt.Sprintf(urlCommands[handleUserPwdCommand], usersPath, userIDParam, privilegePath)
76 | service.Route(service.PATCH(str).
77 | Filter(l.st.SuperUserFilter).
78 | To(l.restUpdatePrivilege).
79 | Doc("Update Account Management privilege").
80 | Operation("updatePrivilege").
81 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
82 | Reads(privilegeInfo{}).
83 | Writes(cr.URL{}))
84 |
85 | str = fmt.Sprintf(urlCommands[handleUserPwdCommand], usersPath, userIDParam, pwdPath)
86 | service.Route(service.PATCH(str).
87 | Filter(l.st.SameUserUpdatePasswordFilter).
88 | To(l.restUpdatePwd).
89 | Doc("Update Account Management password").
90 | Operation("updatePwd").
91 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
92 | Reads(cr.UpdateSecret{}).
93 | Writes(cr.URL{}))
94 |
95 | str = fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
96 | service.Route(service.PATCH(str).
97 | Filter(l.st.SuperUserFilter).
98 | To(l.restResetPwd).
99 | Doc("Reset user password").
100 | Operation("resetPwd").
101 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
102 | Writes(cr.Secret{}))
103 |
104 | str = fmt.Sprintf(urlCommands[handleAuthenticateCommand], verifyPath)
105 | service.Route(service.GET(str).
106 | Filter(l.st.VerifyToken).
107 | To(l.restVerifyToken).
108 | Doc("Verify token").
109 | Operation("verifyToken"))
110 | }
111 |
112 | // RegisterFull : register the all the accounts interfaces to the RESTFul API container
113 | func (l AmRestful) RegisterFull(container *restful.Container) {
114 | servicePath = cr.ServicePathPrefix + cr.Version + amPrefix
115 |
116 | service := new(restful.WebService)
117 | service.
118 | Path(servicePath).
119 | Consumes(restful.MIME_JSON).
120 | Produces(restful.MIME_JSON)
121 | // .Doc("Account Management and Authentication")
122 |
123 | l.setAmRoute(service)
124 | l.setFullRoute(service)
125 | container.Add(service)
126 | }
127 |
128 | // RegisterBasic : register the accounts login/logout to the RESTFul API container
129 | func (l AmRestful) RegisterBasic(container *restful.Container) {
130 | servicePath = cr.ServicePathPrefix + cr.Version + amPrefix
131 |
132 | service := new(restful.WebService)
133 | service.
134 | Path(servicePath).
135 | Consumes(restful.MIME_JSON).
136 | Produces(restful.MIME_JSON)
137 | //.Doc("Account Management and Authentication")
138 |
139 | l.setAmRoute(service)
140 | container.Add(service)
141 | }
142 |
--------------------------------------------------------------------------------
/restful/ocra-restful/ocra_command.go:
--------------------------------------------------------------------------------
1 | package ocraRestful
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/emicklei/go-restful"
7 | "github.com/ibm-security-innovation/libsecurity-go/ocra"
8 | cr "github.com/ibm-security-innovation/libsecurity-go/restful/common-restful"
9 | )
10 |
11 | const (
12 | handleUserCommand = iota
13 | handleUserUpdateCommand
14 | verifyUserIdentityCommand
15 | )
16 |
17 | var (
18 | commandsToPath = []cr.ComamndsToPath{
19 | {handleUserCommand, "%v/{%v}"},
20 | {handleUserUpdateCommand, "%v/{%v}/%v"},
21 | {verifyUserIdentityCommand, "%v/{%v}/%v"},
22 | }
23 |
24 | urlCommands = make(cr.CommandToPath)
25 | )
26 |
27 | func initCommandToPath() {
28 | for _, c := range commandsToPath {
29 | urlCommands[c.Command] = c.Path
30 | }
31 | }
32 |
33 | func (o OcraRestful) setRoute(service *restful.WebService) {
34 | str := fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
35 | service.Route(service.PUT(str).
36 | Filter(o.st.SuperUserFilter).
37 | To(o.restAddOcra).
38 | Doc("Add OCRA").
39 | Operation("addOcra").
40 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
41 | Reads(ocraUserData{}).
42 | Writes(cr.URL{}))
43 |
44 | str = fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
45 | service.Route(service.GET(str).
46 | Filter(o.st.SameUserFilter).
47 | To(o.restGetOcra).
48 | Doc("Get OCRA").
49 | Operation("getOcra").
50 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
51 | Writes(ocra.UserOcra{}))
52 |
53 | str = fmt.Sprintf(urlCommands[handleUserCommand], usersPath, userIDParam)
54 | service.Route(service.DELETE(str).
55 | Filter(o.st.SuperUserFilter).
56 | To(o.restDeleteOcra).
57 | Doc("Remove OCRA").
58 | Operation("deleteOcra").
59 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")))
60 |
61 | str = fmt.Sprintf(urlCommands[handleUserUpdateCommand], usersPath, userIDParam, keyToken)
62 | service.Route(service.PATCH(str).
63 | Filter(o.st.SuperUserFilter).
64 | To(o.restUpdateOcraKey).
65 | Doc("Update OCRA secret key").
66 | Operation("updtaeKey").
67 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
68 | Reads(cr.Secret{}).
69 | Writes(cr.URL{}))
70 |
71 | str = fmt.Sprintf(urlCommands[handleUserUpdateCommand], usersPath, userIDParam, ocraSuiteToken)
72 | service.Route(service.PATCH(str).
73 | Filter(o.st.SameUserFilter).
74 | To(o.restUpdateOcraSuite).
75 | Doc("Update OCRA suite").
76 | Operation("updtaeOcraSuite").
77 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
78 | Reads(cr.StringMessage{}).
79 | Writes(cr.URL{}))
80 |
81 | str = fmt.Sprintf(urlCommands[verifyUserIdentityCommand], usersPath, userIDParam, verifyUserIdentityChallengeToken)
82 | service.Route(service.GET(str).
83 | To(o.restVerifyOcraUserIdentityChallenge).
84 | Doc("Verify the user identity using OCRA, one way").
85 | Operation("verifyUserIdentity").
86 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
87 | Writes(ocraData{}))
88 |
89 | str = fmt.Sprintf(urlCommands[verifyUserIdentityCommand], usersPath, userIDParam, verifyUserIdentityOtpToken)
90 | service.Route(service.PUT(str).
91 | To(o.restVerifyOcraUserIdentityCheckOtp).
92 | Doc("Check the user OTP challenge response using OCRA").
93 | Operation("verifyUserIdentityOtp").
94 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
95 | Reads(ocraData{}).
96 | Writes(cr.Match{}))
97 |
98 | str = fmt.Sprintf(urlCommands[verifyUserIdentityCommand], usersPath, userIDParam, verifyUserIdentityMutualChallengeStep1Token)
99 | service.Route(service.PUT(str).
100 | To(o.restVerifyOcraUserIdentityMutualChallengeStep1).
101 | Doc("Verify the user identity using OCRA, mutual chalange, Step 1").
102 | Operation("verifyUserIdentityMutualChallenge").
103 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
104 | Reads(cr.StringMessage{}).
105 | Writes(ocraData{}))
106 |
107 | str = fmt.Sprintf(urlCommands[verifyUserIdentityCommand], usersPath, userIDParam, verifyUserIdentityMutualChallengeStep2Token)
108 | service.Route(service.PUT(str).
109 | To(o.restVerifyOcraUserIdentityMutualChallengeStep2).
110 | Doc("Verify the user identity using OCRA, mutual chalange, Step 2").
111 | Operation("verifyUserIdentityMutualChallenge").
112 | Param(service.PathParameter(userIDParam, userNameComment).DataType("string")).
113 | Reads(ocraData{}).
114 | Writes(cr.Match{}))
115 | }
116 |
117 | // RegisterBasic : register the OCRA to the RESTFul API container
118 | func (o OcraRestful) RegisterBasic(container *restful.Container) {
119 | servicePath = cr.ServicePathPrefix + cr.Version + ocraPrefix
120 |
121 | service := new(restful.WebService)
122 | service.
123 | Path(servicePath).
124 | Consumes(restful.MIME_JSON).
125 | Produces(restful.MIME_JSON)
126 | //.Doc("OATH Challenge-Response Algorithm")
127 |
128 | o.setRoute(service)
129 | container.Add(service)
130 | }
131 |
--------------------------------------------------------------------------------
/restful/storage-restful/secureStorage_restful.go:
--------------------------------------------------------------------------------
1 | package storageRestful
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "net/http"
7 |
8 | "github.com/emicklei/go-restful"
9 | cr "github.com/ibm-security-innovation/libsecurity-go/restful/common-restful"
10 | "github.com/ibm-security-innovation/libsecurity-go/restful/libsecurity-restful"
11 | ss "github.com/ibm-security-innovation/libsecurity-go/storage"
12 | )
13 |
14 | const (
15 | sPrefix = "/securestorage"
16 | storagePath = "/storage"
17 | storageItemPath = "/item"
18 |
19 | secretIDParam = "secret"
20 | secretComment = "secret val"
21 | keyIDParam = "key-id"
22 | keyComment = "key val"
23 | )
24 |
25 | var (
26 | servicePath string // = cr.ServicePathPrefix + sPrefix
27 |
28 | checkSecretStrength = true // Allow only strength secrets
29 | )
30 |
31 | type itemData struct {
32 | Key string
33 | Value string
34 | }
35 |
36 | type itemValue struct {
37 | Data string
38 | }
39 |
40 | // SRestful : Secure Storage restful structure
41 | type SRestful struct {
42 | st *libsecurityRestful.LibsecurityRestful
43 | }
44 |
45 | func init() {
46 | initCommandToPath()
47 | }
48 |
49 | // NewSsRestful : return a pointer to the secure storage Restful structure
50 | func NewSsRestful() *SRestful {
51 | return &SRestful{}
52 | }
53 |
54 | // SetData : initialize the Secure Storage Restful structure
55 | func (s *SRestful) SetData(stR *libsecurityRestful.LibsecurityRestful) {
56 | s.st = stR
57 | }
58 |
59 | func (s SRestful) getURLPath(request *restful.Request) cr.URL {
60 | return cr.URL{URL: fmt.Sprintf("%v", servicePath)}
61 | }
62 |
63 | func (s SRestful) setError(response *restful.Response, httpStatusCode int, err error) {
64 | data, _ := json.Marshal(cr.Error{Code: httpStatusCode, Message: fmt.Sprintf("%v", err)})
65 | response.WriteErrorString(httpStatusCode, string(data))
66 | }
67 |
68 | func (s SRestful) isSecureStorgaeValid(response *restful.Response) bool {
69 | if s.st.SecureStorage == nil {
70 | s.setError(response, http.StatusNotFound, fmt.Errorf("Error: Secure storage must be created first"))
71 | return false
72 | }
73 | return true
74 | }
75 |
76 | func (s SRestful) isSecretMatch(request *restful.Request, response *restful.Response) bool {
77 | if s.isSecureStorgaeValid(response) == false {
78 | return false
79 | }
80 | secret := request.HeaderParameter(secretIDParam)
81 | if s.st.SecureStorage.IsSecretMatch([]byte(secret)) == false {
82 | s.setError(response, http.StatusNotFound, fmt.Errorf("Error: The password that you entered does not match the secure storage password"))
83 | return false
84 | }
85 | return true
86 | }
87 |
88 | func (s *SRestful) restCreateSecureStorage(request *restful.Request, response *restful.Response) {
89 | secret := request.HeaderParameter(secretIDParam)
90 | data, err := ss.NewStorage([]byte(secret), checkSecretStrength)
91 | if err != nil {
92 | s.setError(response, http.StatusBadRequest, err)
93 | return
94 | }
95 | s.st.SecureStorage = data
96 | response.WriteHeaderAndEntity(http.StatusCreated, s.getURLPath(request))
97 | }
98 |
99 | func (s SRestful) restDeleteSecureStorage(request *restful.Request, response *restful.Response) {
100 | if s.isSecureStorgaeValid(response) == false {
101 | return
102 | }
103 | if s.isSecretMatch(request, response) == false {
104 | return
105 | }
106 | s.st.SecureStorage = nil
107 | response.WriteHeader(http.StatusNoContent)
108 | }
109 |
110 | func (s SRestful) restGetSecureStorage(request *restful.Request, response *restful.Response) {
111 | if s.isSecureStorgaeValid(response) == false {
112 | return
113 | }
114 | if s.isSecretMatch(request, response) == false {
115 | return
116 | }
117 | response.WriteHeaderAndEntity(http.StatusOK, s.st.SecureStorage.GetDecryptStorageData())
118 | }
119 |
120 | func (s SRestful) restAddItemToSecureStorage(request *restful.Request, response *restful.Response) {
121 | if s.isSecureStorgaeValid(response) == false {
122 | return
123 | }
124 | if s.isSecretMatch(request, response) == false {
125 | return
126 | }
127 | var item itemData
128 | err := request.ReadEntity(&item)
129 | if err != nil {
130 | s.setError(response, http.StatusBadRequest, err)
131 | return
132 | }
133 |
134 | err = s.st.SecureStorage.AddItem(item.Key, item.Value)
135 | if err != nil {
136 | s.setError(response, http.StatusInternalServerError, err)
137 | return
138 | }
139 | response.WriteHeaderAndEntity(http.StatusCreated, s.getURLPath(request))
140 | }
141 |
142 | func (s SRestful) restGetItemFromSecureStorage(request *restful.Request, response *restful.Response) {
143 | if s.isSecureStorgaeValid(response) == false {
144 | return
145 | }
146 | if s.isSecretMatch(request, response) == false {
147 | return
148 | }
149 | key := request.HeaderParameter(keyIDParam)
150 |
151 | val, err := s.st.SecureStorage.GetItem(key)
152 | if err != nil {
153 | s.setError(response, http.StatusNotFound, err)
154 | return
155 | }
156 | response.WriteHeaderAndEntity(http.StatusOK, itemValue{val})
157 | }
158 |
159 | func (s SRestful) restDeleteItemFromSecureStorage(request *restful.Request, response *restful.Response) {
160 | if s.isSecureStorgaeValid(response) == false {
161 | return
162 | }
163 | if s.isSecretMatch(request, response) == false {
164 | return
165 | }
166 | key := request.HeaderParameter(keyIDParam)
167 | err := s.st.SecureStorage.RemoveItem(key)
168 | if err != nil {
169 | s.setError(response, http.StatusNotFound, err)
170 | return
171 | }
172 | response.WriteHeader(http.StatusNoContent)
173 | }
174 |
--------------------------------------------------------------------------------
/swagger-dist/forewind/app/v1/securestorage:
--------------------------------------------------------------------------------
1 | {
2 | "swaggerVersion": "1.2",
3 | "apiVersion": "",
4 | "basePath": "/",
5 | "resourcePath": "/forewind/app/v1/securestorage",
6 | "apis": [
7 | {
8 | "path": "/forewind/app/v1/securestorage/storage",
9 | "description": "Secure Storage",
10 | "operations": [
11 | {
12 | "type": "common_restful.Url",
13 | "method": "PUT",
14 | "summary": "Create a new secure storage",
15 | "nickname": "CreateSecureStorage",
16 | "parameters": [
17 | {
18 | "type": "string",
19 | "paramType": "header",
20 | "name": "secret",
21 | "description": "secret val",
22 | "required": false,
23 | "allowMultiple": false
24 | }
25 | ],
26 | "produces": [
27 | "application/json"
28 | ],
29 | "consumes": [
30 | "application/json"
31 | ]
32 | },
33 | {
34 | "type": "void",
35 | "method": "DELETE",
36 | "summary": "Remove the current secure storage",
37 | "nickname": "RemoveSecureStorage",
38 | "parameters": [
39 | {
40 | "type": "string",
41 | "paramType": "header",
42 | "name": "secret",
43 | "description": "secret val",
44 | "required": false,
45 | "allowMultiple": false
46 | }
47 | ],
48 | "produces": [
49 | "application/json"
50 | ],
51 | "consumes": [
52 | "application/json"
53 | ]
54 | },
55 | {
56 | "type": "void",
57 | "method": "GET",
58 | "summary": "Get a secure storage",
59 | "nickname": "getSecureStorage",
60 | "parameters": [
61 | {
62 | "type": "string",
63 | "paramType": "header",
64 | "name": "secret",
65 | "description": "secret val",
66 | "required": false,
67 | "allowMultiple": false
68 | }
69 | ],
70 | "produces": [
71 | "application/json"
72 | ],
73 | "consumes": [
74 | "application/json"
75 | ]
76 | }
77 | ]
78 | },
79 | {
80 | "path": "/forewind/app/v1/securestorage/item",
81 | "description": "Secure Storage",
82 | "operations": [
83 | {
84 | "type": "common_restful.Url",
85 | "method": "PATCH",
86 | "summary": "Add a new item to the secure storage",
87 | "nickname": "addANewItemToTheSecureStorage",
88 | "parameters": [
89 | {
90 | "type": "string",
91 | "paramType": "header",
92 | "name": "secret",
93 | "description": "secret val",
94 | "required": false,
95 | "allowMultiple": false
96 | },
97 | {
98 | "type": "storage_restful.itemData",
99 | "paramType": "body",
100 | "name": "body",
101 | "description": "",
102 | "required": true,
103 | "allowMultiple": false
104 | }
105 | ],
106 | "produces": [
107 | "application/json"
108 | ],
109 | "consumes": [
110 | "application/json"
111 | ]
112 | },
113 | {
114 | "type": "storage_restful.itemValue",
115 | "method": "GET",
116 | "summary": "Get an item from the secure storage",
117 | "nickname": "getAnItemFromTheSecureStorage",
118 | "parameters": [
119 | {
120 | "type": "string",
121 | "paramType": "header",
122 | "name": "secret",
123 | "description": "secret val",
124 | "required": false,
125 | "allowMultiple": false
126 | },
127 | {
128 | "type": "string",
129 | "paramType": "header",
130 | "name": "key-id",
131 | "description": "key val",
132 | "required": false,
133 | "allowMultiple": false
134 | }
135 | ],
136 | "produces": [
137 | "application/json"
138 | ],
139 | "consumes": [
140 | "application/json"
141 | ]
142 | },
143 | {
144 | "type": "void",
145 | "method": "DELETE",
146 | "summary": "Delete item from a secure storage",
147 | "nickname": "deleteItemFromSecureStorage",
148 | "parameters": [
149 | {
150 | "type": "string",
151 | "paramType": "header",
152 | "name": "secret",
153 | "description": "secret val",
154 | "required": false,
155 | "allowMultiple": false
156 | },
157 | {
158 | "type": "string",
159 | "paramType": "header",
160 | "name": "key-id",
161 | "description": "key val",
162 | "required": false,
163 | "allowMultiple": false
164 | }
165 | ],
166 | "produces": [
167 | "application/json"
168 | ],
169 | "consumes": [
170 | "application/json"
171 | ]
172 | }
173 | ]
174 | }
175 | ],
176 | "models": {
177 | "common_restful.Url": {
178 | "id": "common_restful.Url",
179 | "required": [
180 | "Url"
181 | ],
182 | "properties": {
183 | "Url": {
184 | "type": "string"
185 | }
186 | }
187 | },
188 | "storage_restful.itemData": {
189 | "id": "storage_restful.itemData",
190 | "required": [
191 | "Key",
192 | "Value"
193 | ],
194 | "properties": {
195 | "Key": {
196 | "type": "string"
197 | },
198 | "Value": {
199 | "type": "string"
200 | }
201 | }
202 | },
203 | "storage_restful.itemValue": {
204 | "id": "storage_restful.itemValue",
205 | "required": [
206 | "Data"
207 | ],
208 | "properties": {
209 | "Data": {
210 | "type": "string"
211 | }
212 | }
213 | }
214 | }
215 | }
--------------------------------------------------------------------------------
/restful/otp-restful/otp_restful.go:
--------------------------------------------------------------------------------
1 | package otpRestful
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "net/http"
7 |
8 | "github.com/emicklei/go-restful"
9 | defs "github.com/ibm-security-innovation/libsecurity-go/defs"
10 | "github.com/ibm-security-innovation/libsecurity-go/otp"
11 | cr "github.com/ibm-security-innovation/libsecurity-go/restful/common-restful"
12 | "github.com/ibm-security-innovation/libsecurity-go/restful/libsecurity-restful"
13 | )
14 |
15 | const (
16 | otpPrefix = "/otp"
17 | usersPath = "/users"
18 |
19 | userIDParam = "user-name"
20 | userNameComment = "user name"
21 | blockedStateToken = "blocked-state"
22 | blockedStateParam = "blocked-state"
23 | verifyHotpTypeParam = "verify-hotp"
24 | verifyTotpTypeParam = "verify-totp"
25 |
26 | originToken = "Origin"
27 |
28 | trueStr = "true"
29 | falseStr = "false"
30 |
31 | blockedStr = "blocked"
32 | notblockedStr = "not blocked"
33 | )
34 |
35 | var (
36 | servicePath string // = cr.ServicePathPrefix + otpPrefix
37 | checkSecretStrength = true // Allow only strength passwords
38 | )
39 |
40 | // OtpRestful : OtpRestful structure
41 | type OtpRestful struct {
42 | st *libsecurityRestful.LibsecurityRestful
43 | }
44 |
45 | type userState struct {
46 | Blocked bool
47 | }
48 |
49 | func init() {
50 | initCommandToPath()
51 | }
52 |
53 | // NewOtpRestful : return a pointer to the OtpRestful structure
54 | func NewOtpRestful() *OtpRestful {
55 | return &OtpRestful{}
56 | }
57 |
58 | // SetData : initialize the OtpRestful structure
59 | func (u *OtpRestful) SetData(stR *libsecurityRestful.LibsecurityRestful) {
60 | u.st = stR
61 | }
62 |
63 | func (u OtpRestful) getURLPath(request *restful.Request, name string) cr.URL {
64 | // return cr.URL{URL: fmt.Sprintf("%v%v/%v", request.Request.Header.Get(originToken), servicePath, name)}
65 | return cr.URL{URL: fmt.Sprintf("%v/%v", servicePath, name)}
66 | }
67 |
68 | func (u OtpRestful) setError(response *restful.Response, httpStatusCode int, err error) {
69 | data, _ := json.Marshal(cr.Error{Code: httpStatusCode, Message: fmt.Sprintf("%v", err)})
70 | response.WriteErrorString(httpStatusCode, string(data))
71 | }
72 |
73 | func (u OtpRestful) getOtp(request *restful.Request, response *restful.Response) *otp.UserInfoOtp {
74 | userName := request.PathParameter(userIDParam)
75 | data, err := cr.GetPropertyData(userName, defs.OtpPropertyName, u.st.UsersList)
76 | if err != nil {
77 | u.setError(response, http.StatusNotFound, err)
78 | return nil
79 | }
80 | return data.(*otp.UserInfoOtp)
81 | }
82 |
83 | func (u OtpRestful) restAddOtp(request *restful.Request, response *restful.Response) {
84 | var secret cr.Secret
85 | name := request.PathParameter(userIDParam)
86 | err := request.ReadEntity(&secret)
87 | if err != nil {
88 | u.setError(response, http.StatusBadRequest, err)
89 | return
90 | }
91 | data, err := otp.NewSimpleOtpUser([]byte(secret.Secret), checkSecretStrength)
92 | if err != nil {
93 | u.setError(response, http.StatusBadRequest, err)
94 | return
95 | }
96 | err = u.st.UsersList.AddPropertyToEntity(name, defs.OtpPropertyName, data)
97 | if err != nil {
98 | u.setError(response, http.StatusNotFound, err)
99 | return
100 | }
101 | response.WriteHeaderAndEntity(http.StatusCreated, u.getURLPath(request, name))
102 | }
103 |
104 | func (u OtpRestful) restGetOtp(request *restful.Request, response *restful.Response) {
105 | data := u.getOtp(request, response)
106 | if data == nil {
107 | return
108 | }
109 | response.WriteHeaderAndEntity(http.StatusOK, data)
110 | }
111 |
112 | func (u OtpRestful) restDeleteOtp(request *restful.Request, response *restful.Response) {
113 | name := request.PathParameter(userIDParam)
114 | err := u.st.UsersList.RemovePropertyFromEntity(name, defs.OtpPropertyName)
115 | if err != nil {
116 | u.setError(response, http.StatusNotFound, err)
117 | } else {
118 | response.WriteHeader(http.StatusNoContent)
119 | }
120 | }
121 |
122 | func (u OtpRestful) restIsOtpBlocked(request *restful.Request, response *restful.Response) {
123 | var state userState
124 |
125 | data := u.getOtp(request, response)
126 | if data == nil {
127 | return
128 | }
129 | ok, err := data.IsOtpUserBlocked()
130 | state.Blocked = ok
131 | if err != nil {
132 | u.setError(response, http.StatusNotFound, err)
133 | return
134 | }
135 | response.WriteHeaderAndEntity(http.StatusOK, state)
136 | }
137 |
138 | func (u OtpRestful) restSetOtpBlockedState(request *restful.Request, response *restful.Response) {
139 | var blockedState userState
140 |
141 | name := request.PathParameter(userIDParam)
142 | err := request.ReadEntity(&blockedState)
143 | if err != nil {
144 | u.setError(response, http.StatusBadRequest, err)
145 | return
146 | }
147 | data := u.getOtp(request, response)
148 | if data == nil {
149 | return
150 | }
151 | err = data.SetOtpUserBlockedState(blockedState.Blocked)
152 | if err != nil {
153 | u.setError(response, http.StatusNotFound, err)
154 | return
155 | }
156 | response.WriteHeaderAndEntity(http.StatusOK, u.getURLPath(request, name))
157 | }
158 |
159 | func (u OtpRestful) restVerifyOtpHotpUserCode(request *restful.Request, response *restful.Response) {
160 | u.verifyUserOtp(request, response, otp.HotpType)
161 | }
162 |
163 | func (u OtpRestful) restVerifyOtpTotpUserCode(request *restful.Request, response *restful.Response) {
164 | u.verifyUserOtp(request, response, otp.TotpType)
165 | }
166 |
167 | func (u OtpRestful) verifyUserOtp(request *restful.Request, response *restful.Response, otpType otp.TypeOfOtp) {
168 | var secret cr.Secret
169 |
170 | err := request.ReadEntity(&secret)
171 | if err != nil {
172 | u.setError(response, http.StatusBadRequest, err)
173 | return
174 | }
175 | data := u.getOtp(request, response)
176 | if data == nil {
177 | return
178 | }
179 | ok, err := data.VerifyOtpUserCode(secret.Secret, otpType)
180 | res := cr.Match{Match: ok, Message: cr.NoMessageStr}
181 | if ok == false && err != nil {
182 | res.Message = fmt.Sprintf("%v", err)
183 | }
184 | response.WriteHeaderAndEntity(http.StatusOK, res)
185 | }
186 |
--------------------------------------------------------------------------------
/salt/salt.go:
--------------------------------------------------------------------------------
1 | // Package salt : The salt package provides salting services for anyone who uses passwords
2 | package salt
3 |
4 | import (
5 | "bytes"
6 | "crypto/rand"
7 | "crypto/sha1"
8 | "fmt"
9 | "hash"
10 | "io"
11 |
12 | logger "github.com/ibm-security-innovation/libsecurity-go/logger"
13 | )
14 |
15 | const (
16 | defaultOutputLen = 128
17 | defaultSaltLen = 8
18 | defaultNumOfItterations = 1
19 |
20 | minOutputLen = 6
21 | minSaltLen = 0
22 | maxSaltLen = 128
23 | minNumOfItterations = 1
24 | )
25 |
26 | var defaultHashFunc = sha1.New
27 |
28 | // Salt : structure that holds all the parameters relevant to handle salting of password
29 | type Salt struct {
30 | Secret []byte
31 | Salt []byte
32 | OutputLen int // Number of digits in the code. Default is 6
33 | Iterations int // Number of iterations to run the hash function, Default is 64
34 | Digest func() hash.Hash // Digest type, Default is sha1
35 | }
36 |
37 | func (s Salt) String() string {
38 | ret := fmt.Sprintf("Salt info: secret: %v, salt: %v, iterations: %v, output len: %v, digest: %v",
39 | string(s.Secret), string(s.Salt), s.Iterations, s.OutputLen, s.Digest)
40 | return ret
41 | }
42 |
43 | func isOutputLenValid(val int) error {
44 | if val < minOutputLen {
45 | return fmt.Errorf("Salt struct is not valid, The output length %v is less than the minimum %v", val, minOutputLen)
46 | }
47 | return nil
48 | }
49 |
50 | func isDigestValid(digest func() hash.Hash) error {
51 | if digest == nil {
52 | return fmt.Errorf("Salt struct is not valid, It must have a hash function, but the current hash is nil")
53 | }
54 | return nil
55 | }
56 |
57 | func isSecretValid(secret []byte, minSecretLen int, maxSecretLen int) error {
58 | if len(secret) < minSecretLen || len(secret) > maxSecretLen {
59 | return fmt.Errorf("Secret string has illegal length %v, The length must be between %v and %v", len(secret), minSecretLen, maxSecretLen)
60 | }
61 | return nil
62 | }
63 |
64 | func isSaltValid(salt []byte) error {
65 | if len(salt) < minSaltLen || len(salt) > maxSaltLen {
66 | return fmt.Errorf("Salt string has illegal length %v, The length must be between %v and %v", len(salt), minSaltLen, maxSaltLen)
67 | }
68 | return nil
69 | }
70 |
71 | func isNumOfIterationsValid(val int) error {
72 | if val < minNumOfItterations {
73 | return fmt.Errorf("Salt struct is not valid, The number of iterations %v is less than the minimum %v", val, minNumOfItterations)
74 | }
75 | return nil
76 | }
77 |
78 | func (s Salt) isValid(minSecretLen int, maxSecretLen int) error {
79 | err := isSecretValid(s.Secret, minSecretLen, maxSecretLen)
80 | if err != nil {
81 | return err
82 | }
83 | err = isSaltValid(s.Salt)
84 | if err != nil {
85 | return err
86 | }
87 | err = isOutputLenValid(s.OutputLen)
88 | if err != nil {
89 | return err
90 | }
91 | err = isNumOfIterationsValid(s.Iterations)
92 | if err != nil {
93 | return err
94 | }
95 | err = isDigestValid(s.Digest)
96 | if err != nil {
97 | return err
98 | }
99 | return nil
100 | }
101 |
102 | // NewSalt : The default Salt: use sha1, output length 16 bytes
103 | func NewSalt(secret []byte, minSecretLen int, maxSecretLen int, salt []byte) (*Salt, error) {
104 | err := isSecretValid(secret, minSecretLen, maxSecretLen)
105 | if err != nil {
106 | return nil, err
107 | }
108 | err = isSaltValid(salt)
109 | if err != nil {
110 | return nil, err
111 | }
112 | return &Salt{
113 | secret,
114 | salt,
115 | defaultOutputLen,
116 | defaultNumOfItterations,
117 | defaultHashFunc,
118 | }, nil
119 | }
120 |
121 | // GenerateSaltedPassword : generate a salted password using the given password and salt information
122 | func GenerateSaltedPassword(pwd []byte, minSecretLen int, maxSecretLen int, saltData []byte, passwordLen int) ([]byte, error) {
123 | mySalt, err := NewSalt([]byte(pwd), minSecretLen, maxSecretLen, saltData)
124 | if err != nil {
125 | return nil, err
126 | }
127 | if passwordLen != -1 {
128 | mySalt.OutputLen = passwordLen
129 | }
130 | return mySalt.Generate(minSecretLen, maxSecretLen)
131 | }
132 |
133 | // GeneratePasswordWithRndSalt : Return a generated salted password and the used salt from a given password
134 | func GeneratePasswordWithRndSalt(pass string, minSecretLen int, maxSecretLen int) ([]byte, []byte, error) {
135 | salt, err := GetRandomSalt(defaultSaltLen)
136 | if err != nil {
137 | return nil, nil, err
138 | }
139 | s, err := NewSalt([]byte(pass), minSecretLen, maxSecretLen, salt)
140 | if err != nil {
141 | return nil, nil, err
142 | }
143 | saltedPass, err := s.Generate(minSecretLen, maxSecretLen)
144 | return saltedPass, salt, err
145 | }
146 |
147 | // Generate : Return the encrypted data for a given salt and secret
148 | // The way to add salt is: secret + salt
149 | //TODO: output len from right or from left
150 | func (s Salt) Generate(minSecretLen int, maxSecretLen int) ([]byte, error) {
151 | err := s.isValid(minSecretLen, maxSecretLen)
152 | if err != nil {
153 | return []byte(""), err
154 | }
155 | h := s.Digest()
156 |
157 | data := s.Secret
158 | for i := 0; i < s.Iterations; i++ {
159 | data = append(data, s.Salt...)
160 | h.Write(data)
161 | data = h.Sum(nil)
162 | }
163 | logger.Trace.Println("data:", data)
164 | len := len(data)
165 | if len > s.OutputLen {
166 | len = s.OutputLen
167 | }
168 | ret := data[0:len]
169 | return ret, nil
170 | }
171 |
172 | // Match : compare 2 given salt information
173 | func (s Salt) Match(ref []byte, minSecretLen int, maxSecretLen int) (bool, error) {
174 | res, _ := s.Generate(minSecretLen, maxSecretLen)
175 | ok := bytes.Equal(res, ref)
176 | return ok, nil
177 | }
178 |
179 | // GetRandomSalt : generate a random salt with the given length
180 | func GetRandomSalt(size int) ([]byte, error) {
181 | if size < 0 {
182 | return nil, fmt.Errorf("Size %v not valid, Size must be larger than 1", size)
183 | }
184 | buf := make([]byte, size)
185 | _, err := io.ReadFull(rand.Reader, buf)
186 | if err != nil {
187 | panic(fmt.Errorf("Random read failed: %v", err))
188 | }
189 | return buf, nil
190 | }
191 |
--------------------------------------------------------------------------------
/storage/secureStorage_test.go:
--------------------------------------------------------------------------------
1 | package storage
2 |
3 | import (
4 | "bytes"
5 | "crypto/rand"
6 | "io"
7 | "io/ioutil"
8 | "os"
9 | "reflect"
10 | "testing"
11 |
12 | logger "github.com/ibm-security-innovation/libsecurity-go/logger"
13 | )
14 |
15 | const (
16 | baseSecret = "AaBb@1234567890123456"
17 | baseSecret1 = "AaBc;1234567890111111"
18 | )
19 |
20 | var (
21 | RandomStr string
22 | )
23 |
24 | func init() {
25 | val := make([]byte, 20)
26 | io.ReadFull(rand.Reader, val)
27 | RandomStr = string(bytes.Replace(val, []byte{0}, []byte{'a'}, -1))
28 | }
29 |
30 | // Verify that adding the same item key with different values reflects it the results
31 | // Verify that getting a removed item will return an error
32 | func Test_checkAddRemoveItemToSecureStorage(t *testing.T) {
33 | keys := []string{"k1", "k2 is the best key in here", "k1", RandomStr}
34 | values := []string{"v1", "v2 is the answer to the question of k2", "v3", RandomStr}
35 | secret := []byte(baseSecret)
36 |
37 | s, _ := NewStorage(secret, true)
38 | for i, key := range keys {
39 | s.AddItem(key, values[i])
40 | val, err := s.GetItem(key)
41 | if err != nil {
42 | t.Errorf("Test fail: Valid key: '%v' (idx %v) was not presented, error: %v", key, i, err)
43 | } else if val != values[i] {
44 | t.Errorf("Test fail: The recieved value: '%v' was not as expected '%v'", []byte(val), []byte(values[i]))
45 | }
46 | }
47 | for i, key := range keys {
48 | val, err := s.GetItem(key)
49 | if err != nil {
50 | t.Errorf("Test fail: Valid key: '%v' (idx %v) was not presented, error: %v", key, i, err)
51 | } else if val != values[i] && i != 0 {
52 | t.Errorf("Test fail: The recieved value: '%v' was not as expected '%v'", val, values[i])
53 | } else if val == values[i] && i == 0 {
54 | t.Errorf("Test fail: The recieved value: '%v' for key '%v' was changed", val, key)
55 | }
56 | }
57 | s1 := s.GetDecryptStorageData()
58 | for key, val := range s1.Data {
59 | sVal, err := s.GetItem(key)
60 | if err != nil {
61 | t.Errorf("Test fail: decrypted key '%v' was not found in the storage, error: %v", key, err)
62 | }
63 | if val != sVal {
64 | t.Errorf("Test fail: key '%v' decrepted val '%v' is not equal to the one in the storage %v\n", key, val, sVal)
65 | t.Errorf("Storage %v", s.GetDecryptStorageData())
66 | t.Errorf("Decrypted storage %v", s1)
67 | }
68 | }
69 | for _, key := range keys {
70 | s.RemoveItem(key)
71 | _, err := s.GetItem(key)
72 | if err == nil {
73 | t.Errorf("Test fail: Removed key: '%v' return a value", key)
74 | }
75 | }
76 | }
77 |
78 | // Verify that secure storage saved to file is equal to the one loaded from that file
79 | // Verify that wrong secret return an error when reading a secure storage
80 | // Verify that wrong signature return an error when reading a secure storage
81 | func Test_checkStoreLoadSecureStorageFile(t *testing.T) {
82 | keys := []string{"k1", "k2", "k3"}
83 | values := []string{"v1", "v2", "v3"}
84 | secret := []byte(baseSecret)
85 | fileName := "./tmp.txt"
86 | secret1 := []byte(baseSecret1)
87 |
88 | s, _ := NewStorage(secret, true)
89 | for i, key := range keys {
90 | s.AddItem(key, values[i])
91 | }
92 | s.StoreInfo(fileName)
93 | defer os.Remove(fileName)
94 | s1, err := LoadInfo(fileName, secret)
95 | if err != nil {
96 | t.Errorf("Test fail: Read secure storage from file fail, error: %v", err)
97 | t.FailNow()
98 | }
99 | if reflect.DeepEqual(s.Data, s1.Data) == false {
100 | s1.secret = secret // to allow decryption
101 | t.Errorf("Test fail: The original secure storage: '%v' is not equal after store and load it: '%v'",
102 | s.GetDecryptStorageData(), s1.GetDecryptStorageData())
103 | }
104 |
105 | _, err = LoadInfo(fileName, secret1)
106 | if err == nil {
107 | t.Errorf("Test fail: Successfully read secure storage from file while using wrong secret")
108 | }
109 |
110 | // currpt the file data
111 | data, _ := ioutil.ReadFile(fileName)
112 | data[40] = 'x'
113 | data[41] = 'x'
114 | ioutil.WriteFile(fileName, data, FilePermissions)
115 |
116 | _, err = LoadInfo(fileName, secret)
117 | if err == nil {
118 | t.Errorf("Test fail: Successfully read secure storage from file while the file was currpted")
119 | }
120 | }
121 |
122 | // Verify that when the version of the saved file is not equal to the loaded one, an error is generated
123 | func Test_checkVersionInSecureStorageFile(t *testing.T) {
124 | keys := []string{"k1", "k2", "k3"}
125 | values := []string{"v1", "v2", "v3"}
126 | secret := []byte(baseSecret)
127 | fileName := "./tmp.txt"
128 |
129 | s, _ := NewStorage(secret, true)
130 | s.Version = "new 1"
131 | for i, key := range keys {
132 | s.AddItem(key, values[i])
133 | }
134 | s.StoreInfo(fileName)
135 | defer os.Remove(fileName)
136 | _, err := LoadInfo(fileName, secret)
137 | if err == nil {
138 | t.Errorf("Test fail: The saved and loaded versions must not be equal")
139 | t.FailNow()
140 | }
141 | }
142 |
143 | func Test_corners(t *testing.T) {
144 | logger.Init(ioutil.Discard, ioutil.Discard, ioutil.Discard, ioutil.Discard)
145 | s, _ := NewStorage([]byte(baseSecret), true)
146 | s.AddItem("key", "value")
147 | logger.Trace.Println("The storage is", s.GetDecryptStorageData())
148 | if s.IsSecretMatch([]byte("a1234")) == true {
149 | t.Errorf("Test fail: wrong secret match to the storage secret")
150 | }
151 | fileName := "tmp.txt"
152 | defer os.Remove(fileName)
153 | fileName1 := "tmp1.txt"
154 | defer os.Remove(fileName1)
155 | ioutil.WriteFile(fileName, []byte("12345678111111111111111111111111111111111111111111111"), os.ModePerm)
156 | ioutil.WriteFile(fileName1, []byte("12345678111111111111111111111111111111111111111111111 "), os.ModePerm)
157 | v1 := GetSecureKey(fileName)
158 | v2 := GetSecureKey(fileName)
159 | v3 := GetSecureKey(fileName1)
160 | if string(v1) != string(v2) {
161 | t.Errorf("Test fail: the same GetSecureKey return 2 different results")
162 | }
163 | if string(v1) == string(v3) {
164 | t.Errorf("Test fail: different inputs to GetSecureKey return the same results")
165 | }
166 | _, err := NewStorage([]byte("1234"), true)
167 | if err == nil {
168 | t.Errorf("Test fail: simple secret was accepted")
169 | }
170 | }
--------------------------------------------------------------------------------
/restful/password-restful/pwd_restful.go:
--------------------------------------------------------------------------------
1 | package passwordRestful
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "net/http"
7 |
8 | "github.com/emicklei/go-restful"
9 | defs "github.com/ibm-security-innovation/libsecurity-go/defs"
10 | "github.com/ibm-security-innovation/libsecurity-go/password"
11 | cr "github.com/ibm-security-innovation/libsecurity-go/restful/common-restful"
12 | "github.com/ibm-security-innovation/libsecurity-go/restful/libsecurity-restful"
13 | "github.com/ibm-security-innovation/libsecurity-go/salt"
14 | )
15 |
16 | const (
17 | pwdPrefix = "/password"
18 | usersPath = "/users"
19 | userIDParam = "user-name"
20 | userNameComment = "user name"
21 | resetUserPwdPath = "reset"
22 | )
23 |
24 | var (
25 | servicePath string // = cr.ServicePathPrefix + pwdPrefix
26 |
27 | saltLen = 10
28 |
29 | checkPasswordStrength = true // Allow only strength passwords
30 | )
31 |
32 | // PwdRestful : Pwd restful structure
33 | type PwdRestful struct {
34 | st *libsecurityRestful.LibsecurityRestful
35 | saltStr []byte
36 | }
37 |
38 | type secretData struct {
39 | Password string
40 | }
41 |
42 | type userState struct {
43 | Blocked bool
44 | }
45 |
46 | func init() {
47 | initCommandToPath()
48 | }
49 |
50 | // NewPwdRestful : return a pointer to the PwdRestful structure
51 | func NewPwdRestful() *PwdRestful {
52 | saltStr, _ := salt.GetRandomSalt(saltLen)
53 | return &PwdRestful{nil, saltStr}
54 | }
55 |
56 | // SetData : initialize the PwdRestful structure
57 | func (p *PwdRestful) SetData(stR *libsecurityRestful.LibsecurityRestful) {
58 | p.st = stR
59 | }
60 |
61 | func (p PwdRestful) getURLPath(request *restful.Request, name string) cr.URL {
62 | return cr.URL{URL: fmt.Sprintf("%v/%v", servicePath, name)}
63 | }
64 |
65 | func (p PwdRestful) setError(response *restful.Response, httpStatusCode int, err error) {
66 | data, _ := json.Marshal(cr.Error{Code: httpStatusCode, Message: fmt.Sprintf("%v", err)})
67 | response.WriteErrorString(httpStatusCode, string(data))
68 | }
69 |
70 | func (p PwdRestful) getPwdData(request *restful.Request, response *restful.Response) *password.UserPwd {
71 | userName := request.PathParameter(userIDParam)
72 | data, err := cr.GetPropertyData(userName, defs.PwdPropertyName, p.st.UsersList)
73 | if err != nil {
74 | p.setError(response, http.StatusNotFound, err)
75 | return nil
76 | }
77 | return data.(*password.UserPwd)
78 | }
79 |
80 | func (p PwdRestful) restAddPwd(request *restful.Request, response *restful.Response) {
81 | var secret secretData
82 | name := request.PathParameter(userIDParam)
83 |
84 | err := request.ReadEntity(&secret)
85 | if err != nil {
86 | p.setError(response, http.StatusBadRequest, err)
87 | return
88 | }
89 | data, err := password.NewUserPwd([]byte(secret.Password), p.saltStr, checkPasswordStrength)
90 | if err != nil {
91 | p.setError(response, http.StatusBadRequest, err)
92 | return
93 | }
94 | err = p.st.UsersList.AddPropertyToEntity(name, defs.PwdPropertyName, data)
95 | if err != nil {
96 | p.setError(response, http.StatusNotFound, err)
97 | return
98 | }
99 | response.WriteHeaderAndEntity(http.StatusCreated, p.getURLPath(request, name))
100 | }
101 |
102 | func (p PwdRestful) restGetPwd(request *restful.Request, response *restful.Response) {
103 | data := p.getPwdData(request, response)
104 | if data == nil {
105 | return
106 | }
107 | response.WriteHeaderAndEntity(http.StatusOK, data)
108 | }
109 |
110 | func (p PwdRestful) restDeletePwd(request *restful.Request, response *restful.Response) {
111 | name := request.PathParameter(userIDParam)
112 | data := p.getPwdData(request, response)
113 | if data == nil {
114 | return
115 | }
116 | err := p.st.UsersList.RemovePropertyFromEntity(name, defs.PwdPropertyName)
117 | if err != nil {
118 | p.setError(response, http.StatusBadRequest, err)
119 | } else {
120 | response.WriteHeader(http.StatusNoContent)
121 | }
122 | }
123 |
124 | func (p PwdRestful) restUpdatePassword(request *restful.Request, response *restful.Response) {
125 | var secrets cr.UpdateSecret
126 | name := request.PathParameter(userIDParam)
127 | err := request.ReadEntity(&secrets)
128 | if err != nil {
129 | p.setError(response, http.StatusBadRequest, err)
130 | return
131 | }
132 | data := p.getPwdData(request, response)
133 | if data == nil {
134 | return
135 | }
136 | tPwd, _ := salt.GenerateSaltedPassword([]byte(secrets.OldPassword), password.MinPasswordLength, password.MaxPasswordLength, p.saltStr, -1)
137 | pass := password.GetHashedPwd(tPwd)
138 | if err != nil {
139 | p.setError(response, http.StatusBadRequest, err)
140 | return
141 | }
142 | _, err = data.UpdatePassword(pass, []byte(secrets.NewPassword), checkPasswordStrength)
143 | if err != nil {
144 | p.setError(response, http.StatusBadRequest, err)
145 | return
146 | }
147 | response.WriteHeaderAndEntity(http.StatusCreated, p.getURLPath(request, name))
148 | }
149 |
150 | func (p PwdRestful) restResetPassword(request *restful.Request, response *restful.Response) {
151 | data := p.getPwdData(request, response)
152 | if data == nil {
153 | return
154 | }
155 | newPwd, err := data.ResetPassword()
156 | if err != nil {
157 | p.setError(response, http.StatusBadRequest, err)
158 | return
159 | }
160 | response.WriteHeaderAndEntity(http.StatusCreated, secretData{string(newPwd)})
161 | }
162 |
163 | func (p PwdRestful) restVerifyPassword(request *restful.Request, response *restful.Response) {
164 | var secret secretData
165 | err := request.ReadEntity(&secret)
166 | tPwd, _ := salt.GenerateSaltedPassword([]byte(secret.Password), password.MinPasswordLength, password.MaxPasswordLength, p.saltStr, -1)
167 | pass := password.GetHashedPwd(tPwd)
168 | if err != nil {
169 | p.setError(response, http.StatusBadRequest, err)
170 | return
171 | }
172 | data := p.getPwdData(request, response)
173 | if data == nil {
174 | return
175 | }
176 | err = data.IsPasswordMatch(pass)
177 | ok := true
178 | if err != nil {
179 | ok = false
180 | }
181 | res := cr.Match{Match: ok, Message: cr.NoMessageStr}
182 | if ok == false && err != nil {
183 | res.Message = fmt.Sprintf("%v", err)
184 | }
185 | response.WriteHeaderAndEntity(http.StatusOK, res)
186 | }
187 |
--------------------------------------------------------------------------------
/restful/acl-restful/acl_command.go:
--------------------------------------------------------------------------------
1 | package aclRestful
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/emicklei/go-restful"
7 | "github.com/ibm-security-innovation/libsecurity-go/acl"
8 | cr "github.com/ibm-security-innovation/libsecurity-go/restful/common-restful"
9 | )
10 |
11 | const (
12 | handleAclCommand = iota
13 | handlePermissionCommand
14 | getAllPermissionCommand
15 | getAllPermissionsOfEntityCommand
16 | )
17 |
18 | var (
19 | commandsToPath = []cr.ComamndsToPath{
20 | {handleAclCommand, "%v/{%v}"},
21 | {handlePermissionCommand, "%v/{%v}/%v/{%v}/%v/{%v}"},
22 | {getAllPermissionCommand, "%v/%v/{%v}"},
23 | {getAllPermissionsOfEntityCommand, "%v/{%v}/%v/{%v}"},
24 | }
25 | urlCommands = make(cr.CommandToPath)
26 | )
27 |
28 | func initCommandToPath() {
29 | for _, c := range commandsToPath {
30 | urlCommands[c.Command] = c.Path
31 | }
32 | }
33 |
34 | func (a AclRestful) setRoute(service *restful.WebService) {
35 | str := fmt.Sprintf(urlCommands[handleAclCommand], resourceToken, resourceNameParam)
36 | service.Route(service.PUT(str).
37 | Filter(a.st.SuperUserFilter).
38 | To(a.restAddAclToResource).
39 | Doc("Add ACL to resource").
40 | Operation("addAclToResource").
41 | Param(service.PathParameter(resourceNameParam, resourceComment).DataType("string")).
42 | Reads(acl.Acl{}).
43 | Writes(cr.URL{}))
44 |
45 | str = fmt.Sprintf(urlCommands[handleAclCommand], resourceToken, resourceNameParam)
46 | service.Route(service.GET(str).
47 | Filter(a.st.SameUserFilter).
48 | To(a.restGetAclOfResource).
49 | Doc("Get ACL attached to resource").
50 | Operation("getAcl").
51 | Param(service.PathParameter(resourceNameParam, resourceComment).DataType("string")).
52 | Writes(acl.Acl{}))
53 |
54 | str = fmt.Sprintf(urlCommands[handleAclCommand], resourceToken, resourceNameParam)
55 | service.Route(service.DELETE(str).
56 | Filter(a.st.SuperUserFilter).
57 | To(a.restDeleteAclFromResource).
58 | Doc("Remove ACL from resource").
59 | Operation("deleteAcl").
60 | Param(service.PathParameter(resourceNameParam, resourceComment).DataType("string")))
61 | }
62 |
63 | func (a AclRestful) setUsersRoute(service *restful.WebService) {
64 | str := fmt.Sprintf(urlCommands[handlePermissionCommand], entityToken, entityNameParam, resourceToken, resourceNameParam, permissionsToken, permissionParam)
65 | service.Route(service.PUT(str).
66 | Filter(a.st.SuperUserFilter).
67 | To(a.restSetPermission).
68 | Doc("Grant the premission to the given entity for a given resource").
69 | Operation("setPermission").
70 | Param(service.PathParameter(entityNameParam, entityComment).DataType("string")).
71 | Param(service.PathParameter(resourceNameParam, resourceComment).DataType("string")).
72 | Param(service.PathParameter(permissionParam, permissionComment).DataType("string")).
73 | Writes(cr.URL{}))
74 |
75 | str = fmt.Sprintf(urlCommands[handlePermissionCommand], entityToken, entityNameParam, resourceToken, resourceNameParam, permissionsToken, permissionParam)
76 | service.Route(service.GET(str).
77 | Filter(a.st.SameUserFilter).
78 | To(a.restCheckPermission).
79 | Doc("Check if the entity has the given permission to the resource").
80 | Operation("checkEntityPermissionToResource").
81 | Param(service.PathParameter(entityNameParam, entityComment).DataType("string")).
82 | Param(service.PathParameter(resourceNameParam, resourceComment).DataType("string")).
83 | Param(service.PathParameter(permissionParam, permissionComment).DataType("string")).
84 | Writes(cr.Match{}))
85 |
86 | str = fmt.Sprintf(urlCommands[handlePermissionCommand], entityToken, entityNameParam, resourceToken, resourceNameParam, permissionsToken, permissionParam)
87 | service.Route(service.DELETE(str).
88 | Filter(a.st.SuperUserFilter).
89 | To(a.restDeletePermission).
90 | Doc("Revoke the permission of the given entity for the given resource").
91 | Operation("deleteEntityPermissionFromAResource").
92 | Param(service.PathParameter(entityNameParam, entityComment).DataType("string")).
93 | Param(service.PathParameter(resourceNameParam, resourceComment).DataType("string")).
94 | Param(service.PathParameter(permissionParam, permissionComment).DataType("string")))
95 |
96 | str = fmt.Sprintf(urlCommands[getAllPermissionCommand], permissionsToken, resourceToken, resourceNameParam)
97 | service.Route(service.GET(str).
98 | Filter(a.st.SameUserFilter).
99 | To(a.restGetAllPermissions).
100 | Doc("Get all the permissions of the given resource").
101 | Operation("getUserGroupPermissions").
102 | Param(service.PathParameter(resourceNameParam, resourceComment).DataType("string")))
103 |
104 | str = fmt.Sprintf(urlCommands[getAllPermissionsOfEntityCommand], entityToken, entityNameParam, resourceToken, resourceNameParam)
105 | service.Route(service.GET(str).
106 | Filter(a.st.SameUserFilter).
107 | To(a.restGetAllPermissionsOfEntity).
108 | Doc("Get all the permissions of the entity").
109 | Operation("getAllEntityPermission").
110 | Param(service.PathParameter(entityNameParam, entityComment).DataType("string")).
111 | Param(service.PathParameter(resourceNameParam, resourceComment).DataType("string")))
112 |
113 | str = fmt.Sprintf(urlCommands[getAllPermissionsOfEntityCommand], resourceToken, resourceNameParam, permissionsToken, permissionParam)
114 | service.Route(service.GET(str).
115 | Filter(a.st.SuperUserFilter).
116 | To(a.restGetWhoUsesAResourcePermission).
117 | Doc("Get all the entities that have the permission to the resource").
118 | Operation("getAllEntitiesOfPermission").
119 | Param(service.PathParameter(resourceNameParam, resourceComment).DataType("string")).
120 | Param(service.PathParameter(permissionParam, permissionComment).DataType("string")))
121 | }
122 |
123 | // RegisterBasic : register the ACL to the RESTFul API container
124 | func (a AclRestful) RegisterBasic(container *restful.Container) {
125 | servicePath = cr.ServicePathPrefix + cr.Version + aclPrefix
126 |
127 | service := new(restful.WebService)
128 | service.
129 | Path(servicePath).
130 | Consumes(restful.MIME_JSON).
131 | Produces(restful.MIME_JSON)
132 | // .Doc("Access Control List")
133 | a.setRoute(service)
134 | a.setUsersRoute(service)
135 | container.Add(service)
136 | }
137 |
--------------------------------------------------------------------------------
/salt/salt_test.go:
--------------------------------------------------------------------------------
1 | package salt
2 |
3 | import (
4 | "crypto/md5"
5 | "crypto/sha1"
6 | "crypto/sha256"
7 | "encoding/hex"
8 | "fmt"
9 | "io/ioutil"
10 | "hash"
11 | "testing"
12 |
13 | logger "github.com/ibm-security-innovation/libsecurity-go/logger"
14 | )
15 |
16 | // TODO not tested: static external tests for iterations other than 1 (I couldn't find an online site)
17 |
18 | const (
19 | testMinPwdLen = 4
20 | testMaxPwdLen = 255
21 | )
22 |
23 | type SaltRun struct {
24 | salt *Salt
25 | result []byte
26 | }
27 |
28 | func (s SaltRun) String() string {
29 | return fmt.Sprintf("%v, result: %v", s.salt, s.result)
30 | }
31 |
32 | var BaseSecret = []byte("ABCD")
33 | var BaseSalt = []byte("A1B2")
34 |
35 | var referenceRunsSalt []SaltRun
36 |
37 | func init() {
38 | referenceRunsSalt = initRefSalt()
39 | }
40 |
41 | func initRefSalt() []SaltRun {
42 | var refRunsSalt []SaltRun
43 | secrets := []string{"", "ABCD", "hello"}
44 | salts := []string{"", "A1B2", "a12b34"}
45 | hashes := []func() hash.Hash{sha1.New, sha256.New, md5.New, nil}
46 | maxOutputLength := minOutputLen + 2
47 |
48 | for _, sc := range secrets {
49 | for _, sl := range salts {
50 | for _, d := range hashes {
51 | for ol := 0; ol < maxOutputLength; ol++ {
52 | for i := minNumOfItterations - 1; i < minNumOfItterations*100; i += 10 {
53 | salt, err := NewSalt([]byte(sc), testMinPwdLen, testMaxPwdLen, []byte(sl))
54 | serr := isSecretValid([]byte(sc), testMinPwdLen, testMaxPwdLen)
55 | slerr := isSaltValid([]byte(sl))
56 | if err == nil && (serr != nil || slerr != nil) {
57 | fmt.Println("Error while initializing")
58 | panic(fmt.Errorf("Initialize failed: initialize was done successfully but the input is invalid: %v", salt))
59 | }
60 | if err != nil && serr == nil && slerr == nil {
61 | fmt.Println("Error while initializing")
62 | panic(fmt.Errorf("Initialize failed: input is valid %v, but error found: %v", salt, err))
63 | }
64 | if err == nil {
65 | salt.OutputLen = ol
66 | salt.Digest = d
67 | salt.Iterations = i
68 | res, err := salt.Generate(testMinPwdLen, testMaxPwdLen)
69 | derr := isDigestValid(d)
70 | olerr := isOutputLenValid(ol)
71 | ierr := isNumOfIterationsValid(i)
72 | if err != nil && derr == nil && olerr == nil && ierr == nil {
73 | fmt.Println("Error while initializing")
74 | panic(fmt.Errorf("Initialize failed: Try to generate salted code for valid input %v, but error found: %v", salt, err))
75 | } else if err == nil && (derr != nil || olerr != nil || ierr != nil) {
76 | fmt.Println("Error while initializing")
77 | panic(fmt.Errorf("Initialize failed salted code was generated successfully for invalid input: %v\n", salt)) // " secret (%v), salt(%v), digest (%v), output len (%v)\n", sc, sl, d, ol))
78 | } else if err == nil {
79 | refRunsSalt = append(refRunsSalt, SaltRun{salt, res})
80 | }
81 | }
82 | }
83 | }
84 | }
85 | }
86 | }
87 | return refRunsSalt
88 | }
89 |
90 | // Verify that Different parameters (secret keys, salt, output len, input, digests) result with different generated password
91 | func Test_SaltParamesChanged(t *testing.T) {
92 | res := make(map[string]SaltRun)
93 | for _, data := range referenceRunsSalt {
94 | _, exists := res[string(data.result)]
95 | if exists {
96 | t.Error("Runs with different salt parameters return the same password:\nFirst run:", res[string(data.result)], "\nSecond run:", data)
97 | } else {
98 | res[string(data.result)] = data
99 | }
100 | }
101 | }
102 |
103 | // Verify that the same parameters (secret keys, salt, output len, input, digests) result with the same generated password
104 | func Test_SaltRepetitation(t *testing.T) {
105 | for _, data := range referenceRunsSalt {
106 | ok, _ := data.salt.Match(data.result, testMinPwdLen, testMaxPwdLen)
107 | if !ok {
108 | t.Error("Runs with the same salt parameters:", data, "return a different password")
109 | }
110 | }
111 | }
112 |
113 | type testHash struct {
114 | Digest func() hash.Hash
115 | Iteration int
116 | Password string
117 | }
118 |
119 | // Results are from http://www.lorem-ipsum.co.uk/hasher.php
120 | func Test_StaticCalculationSalting(t *testing.T) {
121 | var testsForABCDA1B2 = []testHash{{sha1.New, 1, "f877eed103f74c751952861e0630e643c4ec1eaa"},
122 | {md5.New, 1, "dcfdf079664f00c2ad0d1e348b070bd5"},
123 | {sha256.New, 1, "a193d7d1ba2253b712d13a0dd27bd7dfddcf04a6c8d904ae7e0e9ba2ced0f8fb"}}
124 |
125 | for i, test := range testsForABCDA1B2 {
126 | salt, err := NewSalt([]byte("ABCD"), 4, 128, []byte("A1B2"))
127 | if err != nil {
128 | t.Error("Test fail: Can't initialize Salt, error:", err)
129 | t.FailNow()
130 | }
131 | salt.Iterations = test.Iteration
132 | salt.Digest = test.Digest
133 | ref, err := hex.DecodeString(test.Password)
134 | if err != nil {
135 | t.Error("Test fail, can't convert", test.Password, "to bytes")
136 | }
137 | ok, err := salt.Match(ref, testMinPwdLen, testMaxPwdLen)
138 | if err != nil {
139 | t.Error("Test fail, error:", err)
140 | } else if !ok {
141 | t.Error("Test", i, "Fail: Expected external password", ref,
142 | "did not matched calculated password")
143 | }
144 | }
145 | }
146 |
147 | func Test_RandomSalt(t *testing.T) {
148 | for i := -10; i < 1000; i += 10 {
149 | salt, err := GetRandomSalt(i)
150 | if err == nil && i < 0 {
151 | t.Error("Test fail: get random salt:", salt, "for size of:", i, ", but it is illegal")
152 | } else if err != nil && i >= 0 {
153 | t.Error("Test fail: Generating of random salt for size", i, " fail, error:", err)
154 | }
155 | }
156 | }
157 |
158 |
159 | // Test corners: String, logger etc
160 | func Test_corners(t *testing.T) {
161 | pwd := []byte("ABCD12");
162 | ilegalPwd := "AB"
163 | salt := []byte("salt123")
164 | minSecret := 4
165 | maxSecret := 10
166 |
167 | logger.Init(ioutil.Discard, ioutil.Discard, ioutil.Discard, ioutil.Discard)
168 | salt1, _ := NewSalt(pwd, minSecret, maxSecret, salt)
169 | logger.Trace.Println("The salt info is", salt1)
170 | _, err := GenerateSaltedPassword(pwd, minSecret, maxSecret, salt, len(pwd))
171 | if err != nil {
172 | t.Error("Test fail: Can't generate salted password, error:", err)
173 | }
174 | _, _, err = GeneratePasswordWithRndSalt(ilegalPwd, minSecret, maxSecret)
175 | if err == nil {
176 | t.Error("Test fail: Successfully generated password with random salt for ilegal password", ilegalPwd)
177 | }
178 | _, _, err = GeneratePasswordWithRndSalt(string(pwd), minSecret, maxSecret)
179 | if err != nil {
180 | t.Error("Test fail: Can't generate password with random salt, error:", err)
181 | }
182 | }
--------------------------------------------------------------------------------
/swagger-dist/forewind/app/v1/password:
--------------------------------------------------------------------------------
1 | {
2 | "swaggerVersion": "1.2",
3 | "apiVersion": "",
4 | "basePath": "/",
5 | "resourcePath": "/forewind/app/v1/password",
6 | "apis": [
7 | {
8 | "path": "/forewind/app/v1/password/users/{user-name}",
9 | "description": "",
10 | "operations": [
11 | {
12 | "type": "common_restful.Url",
13 | "method": "PUT",
14 | "summary": "Add Password",
15 | "nickname": "AddPwd",
16 | "parameters": [
17 | {
18 | "type": "string",
19 | "paramType": "path",
20 | "name": "user-name",
21 | "description": "user name",
22 | "required": true,
23 | "allowMultiple": false
24 | },
25 | {
26 | "type": "password_restful.secretData",
27 | "paramType": "body",
28 | "name": "body",
29 | "description": "",
30 | "required": true,
31 | "allowMultiple": false
32 | }
33 | ],
34 | "produces": [
35 | "application/json"
36 | ],
37 | "consumes": [
38 | "application/json"
39 | ]
40 | },
41 | {
42 | "type": "password.UserPwd",
43 | "method": "GET",
44 | "summary": "Get Password",
45 | "nickname": "getPwd",
46 | "parameters": [
47 | {
48 | "type": "string",
49 | "paramType": "path",
50 | "name": "user-name",
51 | "description": "user name",
52 | "required": true,
53 | "allowMultiple": false
54 | }
55 | ],
56 | "produces": [
57 | "application/json"
58 | ],
59 | "consumes": [
60 | "application/json"
61 | ]
62 | },
63 | {
64 | "type": "void",
65 | "method": "DELETE",
66 | "summary": "Remove Password",
67 | "nickname": "deletePwd",
68 | "parameters": [
69 | {
70 | "type": "string",
71 | "paramType": "path",
72 | "name": "user-name",
73 | "description": "user name",
74 | "required": true,
75 | "allowMultiple": false
76 | }
77 | ],
78 | "produces": [
79 | "application/json"
80 | ],
81 | "consumes": [
82 | "application/json"
83 | ]
84 | },
85 | {
86 | "type": "common_restful.Url",
87 | "method": "PATCH",
88 | "summary": "Update Password",
89 | "nickname": "updatePassword",
90 | "parameters": [
91 | {
92 | "type": "string",
93 | "paramType": "path",
94 | "name": "user-name",
95 | "description": "user name",
96 | "required": true,
97 | "allowMultiple": false
98 | },
99 | {
100 | "type": "common_restful.UpdateSecret",
101 | "paramType": "body",
102 | "name": "body",
103 | "description": "",
104 | "required": true,
105 | "allowMultiple": false
106 | }
107 | ],
108 | "produces": [
109 | "application/json"
110 | ],
111 | "consumes": [
112 | "application/json"
113 | ]
114 | },
115 | {
116 | "type": "common_restful.Match",
117 | "method": "POST",
118 | "summary": "Verify that a given password is as expected",
119 | "nickname": "verifyPassword",
120 | "parameters": [
121 | {
122 | "type": "string",
123 | "paramType": "path",
124 | "name": "user-name",
125 | "description": "user name",
126 | "required": true,
127 | "allowMultiple": false
128 | },
129 | {
130 | "type": "password_restful.secretData",
131 | "paramType": "body",
132 | "name": "body",
133 | "description": "",
134 | "required": true,
135 | "allowMultiple": false
136 | }
137 | ],
138 | "produces": [
139 | "application/json"
140 | ],
141 | "consumes": [
142 | "application/json"
143 | ]
144 | }
145 | ]
146 | },
147 | {
148 | "path": "/forewind/app/v1/password/users/{user-name}/reset",
149 | "description": "",
150 | "operations": [
151 | {
152 | "type": "password_restful.secretData",
153 | "method": "GET",
154 | "summary": "Reset password",
155 | "nickname": "resetPassword",
156 | "parameters": [
157 | {
158 | "type": "string",
159 | "paramType": "path",
160 | "name": "user-name",
161 | "description": "user name",
162 | "required": true,
163 | "allowMultiple": false
164 | }
165 | ],
166 | "produces": [
167 | "application/json"
168 | ],
169 | "consumes": [
170 | "application/json"
171 | ]
172 | }
173 | ]
174 | }
175 | ],
176 | "models": {
177 | "password_restful.secretData": {
178 | "id": "password_restful.secretData",
179 | "required": [
180 | "Password"
181 | ],
182 | "properties": {
183 | "Password": {
184 | "type": "string"
185 | }
186 | }
187 | },
188 | "common_restful.Url": {
189 | "id": "common_restful.Url",
190 | "required": [
191 | "Url"
192 | ],
193 | "properties": {
194 | "Url": {
195 | "type": "string"
196 | }
197 | }
198 | },
199 | "password.UserPwd": {
200 | "id": "password.UserPwd",
201 | "required": [
202 | "Password",
203 | "Salt",
204 | "Expiration",
205 | "ErrorsCounter",
206 | "OneTimePwd",
207 | "OldPasswords"
208 | ],
209 | "properties": {
210 | "Password": {
211 | "type": "array",
212 | "items": {
213 | "$ref": "integer"
214 | }
215 | },
216 | "Salt": {
217 | "type": "array",
218 | "items": {
219 | "$ref": "integer"
220 | }
221 | },
222 | "Expiration": {
223 | "type": "string",
224 | "format": "date-time"
225 | },
226 | "ErrorsCounter": {
227 | "type": "integer",
228 | "format": "int32"
229 | },
230 | "OneTimePwd": {
231 | "type": "boolean"
232 | },
233 | "OldPasswords": {
234 | "type": "array",
235 | "items": {
236 | "$ref": "password.UserPwd.OldPasswords"
237 | }
238 | }
239 | }
240 | },
241 | "integer": {
242 | "id": "integer",
243 | "properties": {}
244 | },
245 | "password.UserPwd.OldPasswords": {
246 | "id": "password.UserPwd.OldPasswords",
247 | "properties": {}
248 | },
249 | "common_restful.UpdateSecret": {
250 | "id": "common_restful.UpdateSecret",
251 | "required": [
252 | "OldPassword",
253 | "NewPassword"
254 | ],
255 | "properties": {
256 | "OldPassword": {
257 | "type": "string"
258 | },
259 | "NewPassword": {
260 | "type": "string"
261 | }
262 | }
263 | },
264 | "common_restful.Match": {
265 | "id": "common_restful.Match",
266 | "required": [
267 | "Match",
268 | "Message"
269 | ],
270 | "properties": {
271 | "Match": {
272 | "type": "boolean"
273 | },
274 | "Message": {
275 | "type": "string"
276 | }
277 | }
278 | }
279 | }
280 | }
--------------------------------------------------------------------------------
/swagger-dist/lib/highlight.7.3.pack.js:
--------------------------------------------------------------------------------
1 | var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(//gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=(""+o.nodeName.toLowerCase()+">")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"
")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"",rE:true,sL:"css"}},{cN:"tag",b:"