diff --git a/Gopkg.lock b/Gopkg.lock index 0c8438e..ee1b309 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,92 +2,137 @@ [[projects]] + digest = "1:e4b30804a381d7603b8a344009987c1ba351c26043501b23b8c7ce21f0b67474" name = "github.com/BurntSushi/toml" packages = ["."] - revision = "b26d9c308763d68093482582cea63d69be07a0f0" - version = "v0.3.0" + pruneopts = "" + revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005" + version = "v0.3.1" [[projects]] branch = "master" + digest = "1:a7f619ffc7b99687f9444bd0a07509fec8ae708a7175d234878129896c0918a8" name = "github.com/alecthomas/template" - packages = [".","parse"] - revision = "a0175ee3bccc567396460bf5acd36800cb10c49c" + packages = [ + ".", + "parse", + ] + pruneopts = "" + revision = "fb15b899a75114aa79cc930e33c46b577cc664b1" [[projects]] branch = "master" + digest = "1:9d943843b71c5d44f184893fcdbe419bf639fee8647ceeca4c7d4fd95923721c" name = "github.com/alecthomas/units" packages = ["."] - revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" + pruneopts = "" + revision = "f65c72e2690dc4b403c8bd637baf4611cd4c069b" [[projects]] + digest = "1:0deddd908b6b4b768cfc272c16ee61e7088a60f7fe2f06c547bd3d8e1f8b8e77" name = "github.com/davecgh/go-spew" packages = ["spew"] - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" + pruneopts = "" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" [[projects]] - branch = "master" + digest = "1:23a5efa4b272df86a8ebffc942f5e0c1aac4b750836037394cc450b6d91e241a" name = "github.com/fatih/camelcase" packages = ["."] + pruneopts = "" revision = "44e46d280b43ec1531bb25252440e34f1b800b65" + version = "v1.0.0" [[projects]] + digest = "1:2defd14040b1ca18840524e9d0f9cfab0ea8d2f2d396b343d221f8bc8e21ab9d" name = "github.com/fatih/structs" packages = ["."] - revision = "a720dfa8df582c51dee1b36feabb906bde1588bd" - version = "v1.0" + pruneopts = "" + revision = "4966fc68f5b7593aafa6cbbba2d65ec6e1416047" + version = "v1.1.0" [[projects]] - branch = "master" + digest = "1:3d515709370a6e092b7e63efa5e717d2140277fd7094cc467564f438528e1997" name = "github.com/google/go-tpm" - packages = ["tpm","tpmutil"] - revision = "e965e8a65d24d3bb2e9daaf23686b645e0bf76fe" + packages = [ + "tpm", + "tpmutil", + "tpmutil/tbs", + ] + pruneopts = "" + revision = "b2c8857856f7516bb35d299c6b5d19f64e4ad579" + version = "v0.2.0" [[projects]] branch = "master" + digest = "1:e73373a4c63689f660e6a5ae925f3db6a32637de4323dc9c7619165ba0f6afa5" name = "github.com/koding/multiconfig" packages = ["."] + pruneopts = "" revision = "69c27309b2d751c576b59ea9c3726597c2375da3" [[projects]] + digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:4c91d68634e10e2bc14532a66dda447d4059f1986dccafb99a7f2016be4b6b92" name = "github.com/rekby/gpt" packages = ["."] - revision = "a4c9f9f82947e24665761a04fa1aacd75cdecec2" + pruneopts = "" + revision = "520ac65554cffa63270eeb0723c59c08b319a94d" [[projects]] + digest = "1:f7b541897bcde05a04a044c342ddc7425aab7e331f37b47fbb486cd16324b48e" name = "github.com/stretchr/testify" - packages = ["assert","require"] - revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" - version = "v1.2.2" + packages = [ + "assert", + "require", + ] + pruneopts = "" + revision = "221dbe5ed46703ee255b1da0dec05086f5035f62" + version = "v1.4.0" [[projects]] branch = "master" + digest = "1:d14beb2753a51f8d2ccb1ad8af9f6146854f6528b70d0cab2bc19825a4418191" name = "github.com/systemboot/systemboot" - packages = ["pkg/storage","pkg/tpm"] - revision = "a642c9c31e0b58c1bc22fa204cd3cc5008447927" + packages = ["pkg/storage"] + pruneopts = "" + revision = "8ea48ab5b46d5f65e0327d8b9c485c4f05890b29" [[projects]] + digest = "1:15d017551627c8bb091bde628215b2861bed128855343fdd570c62d08871f6e1" name = "gopkg.in/alecthomas/kingpin.v2" packages = ["."] + pruneopts = "" revision = "947dcec5ba9c011838740e680966fd7087a71d0d" version = "v2.2.6" [[projects]] + digest = "1:16c45073e621eb4261d14740daeaf985e53657ebb40b14fc56499c23e876f84a" name = "gopkg.in/yaml.v2" packages = ["."] - revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" - version = "v2.2.1" + pruneopts = "" + revision = "f90ceb4f409096b60e2e9076b38b304b8246e5fa" + version = "v2.2.5" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "66098ff1daba504c2a19897ce0cedb6a326f8a248f47e6e28e7173a76c41780c" + input-imports = [ + "github.com/google/go-tpm/tpm", + "github.com/koding/multiconfig", + "github.com/rekby/gpt", + "github.com/stretchr/testify/require", + "github.com/systemboot/systemboot/pkg/storage", + "gopkg.in/alecthomas/kingpin.v2", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/vendor/github.com/BurntSushi/toml/COPYING b/vendor/github.com/BurntSushi/toml/COPYING index 5a8e332..01b5743 100644 --- a/vendor/github.com/BurntSushi/toml/COPYING +++ b/vendor/github.com/BurntSushi/toml/COPYING @@ -1,14 +1,21 @@ - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 +The MIT License (MIT) - Copyright (C) 2004 Sam Hocevar +Copyright (c) 2013 TOML authors - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING index 5a8e332..01b5743 100644 --- a/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING @@ -1,14 +1,21 @@ - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 +The MIT License (MIT) - Copyright (C) 2004 Sam Hocevar +Copyright (c) 2013 TOML authors - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING index 5a8e332..01b5743 100644 --- a/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING @@ -1,14 +1,21 @@ - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 +The MIT License (MIT) - Copyright (C) 2004 Sam Hocevar +Copyright (c) 2013 TOML authors - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING b/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING index 5a8e332..01b5743 100644 --- a/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING +++ b/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING @@ -1,14 +1,21 @@ - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 +The MIT License (MIT) - Copyright (C) 2004 Sam Hocevar +Copyright (c) 2013 TOML authors - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/BurntSushi/toml/decode_test.go b/vendor/github.com/BurntSushi/toml/decode_test.go index 0c36b33..95bc987 100644 --- a/vendor/github.com/BurntSushi/toml/decode_test.go +++ b/vendor/github.com/BurntSushi/toml/decode_test.go @@ -16,6 +16,8 @@ age = 250 andrew = "gallant" kait = "brady" now = 1987-07-05T05:45:00Z +nowEast = 2017-06-22T16:15:21+08:00 +nowWest = 2017-06-22T02:14:36-06:00 yesOrNo = true pi = 3.14 colors = [ @@ -38,6 +40,8 @@ cauchy = "cat 2" Pi float64 YesOrNo bool Now time.Time + NowEast time.Time + NowWest time.Time Andrew string Kait string My map[string]cats @@ -53,11 +57,21 @@ cauchy = "cat 2" if err != nil { panic(err) } + nowEast, err := time.Parse("2006-01-02T15:04:05-07:00", "2017-06-22T16:15:21+08:00") + if err != nil { + panic(err) + } + nowWest, err := time.Parse("2006-01-02T15:04:05-07:00", "2017-06-22T02:14:36-06:00") + if err != nil { + panic(err) + } var answer = simple{ Age: 250, Andrew: "gallant", Kait: "brady", Now: now, + NowEast: nowEast, + NowWest: nowWest, YesOrNo: true, Pi: 3.14, Colors: [][]string{ diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go index 6dee7fc..e0a742a 100644 --- a/vendor/github.com/BurntSushi/toml/lex.go +++ b/vendor/github.com/BurntSushi/toml/lex.go @@ -775,7 +775,7 @@ func lexDatetime(lx *lexer) stateFn { return lexDatetime } switch r { - case '-', 'T', ':', '.', 'Z': + case '-', 'T', ':', '.', 'Z', '+': return lexDatetime } diff --git a/vendor/github.com/alecthomas/template/go.mod b/vendor/github.com/alecthomas/template/go.mod new file mode 100644 index 0000000..a70670a --- /dev/null +++ b/vendor/github.com/alecthomas/template/go.mod @@ -0,0 +1 @@ +module github.com/alecthomas/template diff --git a/vendor/github.com/alecthomas/units/bytes.go b/vendor/github.com/alecthomas/units/bytes.go index eaadeb8..61d0ca4 100644 --- a/vendor/github.com/alecthomas/units/bytes.go +++ b/vendor/github.com/alecthomas/units/bytes.go @@ -27,6 +27,7 @@ var ( // ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB // and KiB are both 1024. +// However "kB", which is the correct SI spelling of 1000 Bytes, is rejected. func ParseBase2Bytes(s string) (Base2Bytes, error) { n, err := ParseUnit(s, bytesUnitMap) if err != nil { @@ -68,12 +69,13 @@ func ParseMetricBytes(s string) (MetricBytes, error) { return MetricBytes(n), err } +// TODO: represents 1000B as uppercase "KB", while SI standard requires "kB". func (m MetricBytes) String() string { return ToString(int64(m), 1000, "B", "B") } // ParseStrictBytes supports both iB and B suffixes for base 2 and metric, -// respectively. That is, KiB represents 1024 and KB represents 1000. +// respectively. That is, KiB represents 1024 and kB, KB represent 1000. func ParseStrictBytes(s string) (int64, error) { n, err := ParseUnit(s, bytesUnitMap) if err != nil { diff --git a/vendor/github.com/alecthomas/units/bytes_test.go b/vendor/github.com/alecthomas/units/bytes_test.go index 6cbc79d..5bae48e 100644 --- a/vendor/github.com/alecthomas/units/bytes_test.go +++ b/vendor/github.com/alecthomas/units/bytes_test.go @@ -16,6 +16,8 @@ func TestParseBase2Bytes(t *testing.T) { n, err := ParseBase2Bytes("0B") assert.NoError(t, err) assert.Equal(t, 0, int(n)) + n, err = ParseBase2Bytes("1kB") + assert.Error(t, err) n, err = ParseBase2Bytes("1KB") assert.NoError(t, err) assert.Equal(t, 1024, int(n)) @@ -25,10 +27,23 @@ func TestParseBase2Bytes(t *testing.T) { n, err = ParseBase2Bytes("1.5MB") assert.NoError(t, err) assert.Equal(t, 1572864, int(n)) + + n, err = ParseBase2Bytes("1kiB") + assert.Error(t, err) + n, err = ParseBase2Bytes("1KiB") + assert.NoError(t, err) + assert.Equal(t, 1024, int(n)) + n, err = ParseBase2Bytes("1MiB1KiB25B") + assert.NoError(t, err) + assert.Equal(t, 1049625, int(n)) + n, err = ParseBase2Bytes("1.5MiB") + assert.NoError(t, err) + assert.Equal(t, 1572864, int(n)) } func TestMetricBytesString(t *testing.T) { assert.Equal(t, MetricBytes(0).String(), "0B") + // TODO: SI standard prefix is lowercase "kB" assert.Equal(t, MetricBytes(1001).String(), "1KB1B") assert.Equal(t, MetricBytes(1001025).String(), "1MB1KB25B") } @@ -37,6 +52,9 @@ func TestParseMetricBytes(t *testing.T) { n, err := ParseMetricBytes("0B") assert.NoError(t, err) assert.Equal(t, 0, int(n)) + n, err = ParseMetricBytes("1kB") + assert.NoError(t, err) + assert.Equal(t, 1000, int(n)) n, err = ParseMetricBytes("1KB1B") assert.NoError(t, err) assert.Equal(t, 1001, int(n)) @@ -47,3 +65,37 @@ func TestParseMetricBytes(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 1500000, int(n)) } + +func TestParseStrictBytes(t *testing.T) { + n, err := ParseStrictBytes("0B") + assert.NoError(t, err) + assert.Equal(t, 0, int(n)) + + n, err = ParseStrictBytes("1kiB") + assert.Error(t, err) + n, err = ParseStrictBytes("1KiB") + assert.NoError(t, err) + assert.Equal(t, 1024, int(n)) + n, err = ParseStrictBytes("1MiB1KiB25B") + assert.NoError(t, err) + assert.Equal(t, 1049625, int(n)) + n, err = ParseStrictBytes("1.5MiB") + assert.NoError(t, err) + assert.Equal(t, 1572864, int(n)) + + n, err = ParseStrictBytes("0B") + assert.NoError(t, err) + assert.Equal(t, 0, int(n)) + n, err = ParseStrictBytes("1kB") + assert.NoError(t, err) + assert.Equal(t, 1000, int(n)) + n, err = ParseStrictBytes("1KB1B") + assert.NoError(t, err) + assert.Equal(t, 1001, int(n)) + n, err = ParseStrictBytes("1MB1KB25B") + assert.NoError(t, err) + assert.Equal(t, 1001025, int(n)) + n, err = ParseStrictBytes("1.5MB") + assert.NoError(t, err) + assert.Equal(t, 1500000, int(n)) +} diff --git a/vendor/github.com/alecthomas/units/go.mod b/vendor/github.com/alecthomas/units/go.mod new file mode 100644 index 0000000..c7fb91f --- /dev/null +++ b/vendor/github.com/alecthomas/units/go.mod @@ -0,0 +1,3 @@ +module github.com/alecthomas/units + +require github.com/stretchr/testify v1.4.0 diff --git a/vendor/github.com/alecthomas/units/go.sum b/vendor/github.com/alecthomas/units/go.sum new file mode 100644 index 0000000..8fdee58 --- /dev/null +++ b/vendor/github.com/alecthomas/units/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/alecthomas/units/si.go b/vendor/github.com/alecthomas/units/si.go index 8234a9d..99b2fa4 100644 --- a/vendor/github.com/alecthomas/units/si.go +++ b/vendor/github.com/alecthomas/units/si.go @@ -14,13 +14,37 @@ const ( ) func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 { - return map[string]float64{ - shortSuffix: 1, - "K" + suffix: float64(scale), + res := map[string]float64{ + shortSuffix: 1, + // see below for "k" / "K" "M" + suffix: float64(scale * scale), "G" + suffix: float64(scale * scale * scale), "T" + suffix: float64(scale * scale * scale * scale), "P" + suffix: float64(scale * scale * scale * scale * scale), "E" + suffix: float64(scale * scale * scale * scale * scale * scale), } + + // Standard SI prefixes use lowercase "k" for kilo = 1000. + // For compatibility, and to be fool-proof, we accept both "k" and "K" in metric mode. + // + // However, official binary prefixes are always capitalized - "KiB" - + // and we specifically never parse "kB" as 1024B because: + // + // (1) people pedantic enough to use lowercase according to SI unlikely to abuse "k" to mean 1024 :-) + // + // (2) Use of capital K for 1024 was an informal tradition predating IEC prefixes: + // "The binary meaning of the kilobyte for 1024 bytes typically uses the symbol KB, with an + // uppercase letter K." + // -- https://en.wikipedia.org/wiki/Kilobyte#Base_2_(1024_bytes) + // "Capitalization of the letter K became the de facto standard for binary notation, although this + // could not be extended to higher powers, and use of the lowercase k did persist.[13][14][15]" + // -- https://en.wikipedia.org/wiki/Binary_prefix#History + // See also the extensive https://en.wikipedia.org/wiki/Timeline_of_binary_prefixes. + if scale == 1024 { + res["K"+suffix] = float64(scale) + } else { + res["k"+suffix] = float64(scale) + res["K"+suffix] = float64(scale) + } + return res } diff --git a/vendor/github.com/davecgh/go-spew/.travis.yml b/vendor/github.com/davecgh/go-spew/.travis.yml index 984e073..1f4cbf5 100644 --- a/vendor/github.com/davecgh/go-spew/.travis.yml +++ b/vendor/github.com/davecgh/go-spew/.travis.yml @@ -1,14 +1,28 @@ language: go +go_import_path: github.com/davecgh/go-spew go: - - 1.5.4 - - 1.6.3 - - 1.7 + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - tip +sudo: false install: - - go get -v golang.org/x/tools/cmd/cover + - go get -v github.com/alecthomas/gometalinter + - gometalinter --install script: - - go test -v -tags=safe ./spew - - go test -v -tags=testcgo ./spew -covermode=count -coverprofile=profile.cov + - export PATH=$PATH:$HOME/gopath/bin + - export GORACE="halt_on_error=1" + - test -z "$(gometalinter --disable-all + --enable=gofmt + --enable=golint + --enable=vet + --enable=gosimple + --enable=unconvert + --deadline=4m ./spew | tee /dev/stderr)" + - go test -v -race -tags safe ./spew + - go test -v -race -tags testcgo ./spew -covermode=atomic -coverprofile=profile.cov after_success: - go get -v github.com/mattn/goveralls - - export PATH=$PATH:$HOME/gopath/bin - goveralls -coverprofile=profile.cov -service=travis-ci diff --git a/vendor/github.com/davecgh/go-spew/LICENSE b/vendor/github.com/davecgh/go-spew/LICENSE index c836416..bc52e96 100644 --- a/vendor/github.com/davecgh/go-spew/LICENSE +++ b/vendor/github.com/davecgh/go-spew/LICENSE @@ -2,7 +2,7 @@ ISC License Copyright (c) 2012-2016 Dave Collins -Permission to use, copy, modify, and distribute this software for any +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. diff --git a/vendor/github.com/davecgh/go-spew/README.md b/vendor/github.com/davecgh/go-spew/README.md index 2624304..f6ed02c 100644 --- a/vendor/github.com/davecgh/go-spew/README.md +++ b/vendor/github.com/davecgh/go-spew/README.md @@ -1,12 +1,9 @@ go-spew ======= -[![Build Status](https://img.shields.io/travis/davecgh/go-spew.svg)] -(https://travis-ci.org/davecgh/go-spew) [![ISC License] -(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) [![Coverage Status] -(https://img.shields.io/coveralls/davecgh/go-spew.svg)] -(https://coveralls.io/r/davecgh/go-spew?branch=master) - +[![Build Status](https://img.shields.io/travis/davecgh/go-spew.svg)](https://travis-ci.org/davecgh/go-spew) +[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[![Coverage Status](https://img.shields.io/coveralls/davecgh/go-spew.svg)](https://coveralls.io/r/davecgh/go-spew?branch=master) Go-spew implements a deep pretty printer for Go data structures to aid in debugging. A comprehensive suite of tests with 100% test coverage is provided @@ -21,8 +18,7 @@ post about it ## Documentation -[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)] -(http://godoc.org/github.com/davecgh/go-spew/spew) +[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/davecgh/go-spew/spew) Full `go doc` style documentation for the project can be viewed online without installing this package by using the excellent GoDoc site here: diff --git a/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/davecgh/go-spew/spew/bypass.go index 8a4a658..7929947 100644 --- a/vendor/github.com/davecgh/go-spew/spew/bypass.go +++ b/vendor/github.com/davecgh/go-spew/spew/bypass.go @@ -16,7 +16,9 @@ // when the code is not running on Google App Engine, compiled by GopherJS, and // "-tags safe" is not added to the go build command line. The "disableunsafe" // tag is deprecated and thus should not be used. -// +build !js,!appengine,!safe,!disableunsafe +// Go versions prior to 1.4 are disabled because they use a different layout +// for interfaces which make the implementation of unsafeReflectValue more complex. +// +build !js,!appengine,!safe,!disableunsafe,go1.4 package spew @@ -34,80 +36,49 @@ const ( ptrSize = unsafe.Sizeof((*byte)(nil)) ) +type flag uintptr + var ( - // offsetPtr, offsetScalar, and offsetFlag are the offsets for the - // internal reflect.Value fields. These values are valid before golang - // commit ecccf07e7f9d which changed the format. The are also valid - // after commit 82f48826c6c7 which changed the format again to mirror - // the original format. Code in the init function updates these offsets - // as necessary. - offsetPtr = uintptr(ptrSize) - offsetScalar = uintptr(0) - offsetFlag = uintptr(ptrSize * 2) - - // flagKindWidth and flagKindShift indicate various bits that the - // reflect package uses internally to track kind information. - // - // flagRO indicates whether or not the value field of a reflect.Value is - // read-only. - // - // flagIndir indicates whether the value field of a reflect.Value is - // the actual data or a pointer to the data. - // - // These values are valid before golang commit 90a7c3c86944 which - // changed their positions. Code in the init function updates these - // flags as necessary. - flagKindWidth = uintptr(5) - flagKindShift = uintptr(flagKindWidth - 1) - flagRO = uintptr(1 << 0) - flagIndir = uintptr(1 << 1) + // flagRO indicates whether the value field of a reflect.Value + // is read-only. + flagRO flag + + // flagAddr indicates whether the address of the reflect.Value's + // value may be taken. + flagAddr flag ) -func init() { - // Older versions of reflect.Value stored small integers directly in the - // ptr field (which is named val in the older versions). Versions - // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named - // scalar for this purpose which unfortunately came before the flag - // field, so the offset of the flag field is different for those - // versions. - // - // This code constructs a new reflect.Value from a known small integer - // and checks if the size of the reflect.Value struct indicates it has - // the scalar field. When it does, the offsets are updated accordingly. - vv := reflect.ValueOf(0xf00) - if unsafe.Sizeof(vv) == (ptrSize * 4) { - offsetScalar = ptrSize * 2 - offsetFlag = ptrSize * 3 - } +// flagKindMask holds the bits that make up the kind +// part of the flags field. In all the supported versions, +// it is in the lower 5 bits. +const flagKindMask = flag(0x1f) - // Commit 90a7c3c86944 changed the flag positions such that the low - // order bits are the kind. This code extracts the kind from the flags - // field and ensures it's the correct type. When it's not, the flag - // order has been changed to the newer format, so the flags are updated - // accordingly. - upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) - upfv := *(*uintptr)(upf) - flagKindMask := uintptr((1<>flagKindShift != uintptr(reflect.Int) { - flagKindShift = 0 - flagRO = 1 << 5 - flagIndir = 1 << 6 - - // Commit adf9b30e5594 modified the flags to separate the - // flagRO flag into two bits which specifies whether or not the - // field is embedded. This causes flagIndir to move over a bit - // and means that flagRO is the combination of either of the - // original flagRO bit and the new bit. - // - // This code detects the change by extracting what used to be - // the indirect bit to ensure it's set. When it's not, the flag - // order has been changed to the newer format, so the flags are - // updated accordingly. - if upfv&flagIndir == 0 { - flagRO = 3 << 5 - flagIndir = 1 << 7 - } +// Different versions of Go have used different +// bit layouts for the flags type. This table +// records the known combinations. +var okFlags = []struct { + ro, addr flag +}{{ + // From Go 1.4 to 1.5 + ro: 1 << 5, + addr: 1 << 7, +}, { + // Up to Go tip. + ro: 1<<5 | 1<<6, + addr: 1 << 8, +}} + +var flagValOffset = func() uintptr { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") } + return field.Offset +}() + +// flagField returns a pointer to the flag field of a reflect.Value. +func flagField(v *reflect.Value) *flag { + return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) } // unsafeReflectValue converts the passed reflect.Value into a one that bypasses @@ -119,34 +90,56 @@ func init() { // This allows us to check for implementations of the Stringer and error // interfaces to be used for pretty printing ordinarily unaddressable and // inaccessible values such as unexported struct fields. -func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { - indirects := 1 - vt := v.Type() - upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) - rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) - if rvf&flagIndir != 0 { - vt = reflect.PtrTo(v.Type()) - indirects++ - } else if offsetScalar != 0 { - // The value is in the scalar field when it's not one of the - // reference types. - switch vt.Kind() { - case reflect.Uintptr: - case reflect.Chan: - case reflect.Func: - case reflect.Map: - case reflect.Ptr: - case reflect.UnsafePointer: - default: - upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + - offsetScalar) - } +func unsafeReflectValue(v reflect.Value) reflect.Value { + if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { + return v } + flagFieldPtr := flagField(&v) + *flagFieldPtr &^= flagRO + *flagFieldPtr |= flagAddr + return v +} - pv := reflect.NewAt(vt, upv) - rv = pv - for i := 0; i < indirects; i++ { - rv = rv.Elem() +// Sanity checks against future reflect package changes +// to the type or semantics of the Value.flag field. +func init() { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { + panic("reflect.Value flag field has changed kind") + } + type t0 int + var t struct { + A t0 + // t0 will have flagEmbedRO set. + t0 + // a will have flagStickyRO set + a t0 + } + vA := reflect.ValueOf(t).FieldByName("A") + va := reflect.ValueOf(t).FieldByName("a") + vt0 := reflect.ValueOf(t).FieldByName("t0") + + // Infer flagRO from the difference between the flags + // for the (otherwise identical) fields in t. + flagPublic := *flagField(&vA) + flagWithRO := *flagField(&va) | *flagField(&vt0) + flagRO = flagPublic ^ flagWithRO + + // Infer flagAddr from the difference between a value + // taken from a pointer and not. + vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") + flagNoPtr := *flagField(&vA) + flagPtr := *flagField(&vPtrA) + flagAddr = flagNoPtr ^ flagPtr + + // Check that the inferred flags tally with one of the known versions. + for _, f := range okFlags { + if flagRO == f.ro && flagAddr == f.addr { + return + } } - return rv + panic("reflect.Value read-only flag has changed semantics") } diff --git a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go index 1fe3cf3..205c28d 100644 --- a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go +++ b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go @@ -16,7 +16,7 @@ // when the code is running on Google App Engine, compiled by GopherJS, or // "-tags safe" is added to the go build command line. The "disableunsafe" // tag is deprecated and thus should not be used. -// +build js appengine safe disableunsafe +// +build js appengine safe disableunsafe !go1.4 package spew diff --git a/vendor/github.com/davecgh/go-spew/spew/common.go b/vendor/github.com/davecgh/go-spew/spew/common.go index 7c519ff..1be8ce9 100644 --- a/vendor/github.com/davecgh/go-spew/spew/common.go +++ b/vendor/github.com/davecgh/go-spew/spew/common.go @@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) { w.Write(closeParenBytes) } -// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x' +// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' // prefix to Writer w. func printHexPtr(w io.Writer, p uintptr) { // Null pointer. diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/davecgh/go-spew/spew/dump.go index df1d582..f78d89f 100644 --- a/vendor/github.com/davecgh/go-spew/spew/dump.go +++ b/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -35,16 +35,16 @@ var ( // cCharRE is a regular expression that matches a cgo char. // It is used to detect character arrays to hexdump them. - cCharRE = regexp.MustCompile("^.*\\._Ctype_char$") + cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) // cUnsignedCharRE is a regular expression that matches a cgo unsigned // char. It is used to detect unsigned character arrays to hexdump // them. - cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$") + cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) // cUint8tCharRE is a regular expression that matches a cgo uint8_t. // It is used to detect uint8_t arrays to hexdump them. - cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$") + cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) ) // dumpState contains information about the state of a dump operation. @@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) { // Display dereferenced value. d.w.Write(openParenBytes) switch { - case nilFound == true: + case nilFound: d.w.Write(nilAngleBytes) - case cycleFound == true: + case cycleFound: d.w.Write(circularBytes) default: diff --git a/vendor/github.com/davecgh/go-spew/spew/dump_test.go b/vendor/github.com/davecgh/go-spew/spew/dump_test.go index 5aad9c7..4a31a2e 100644 --- a/vendor/github.com/davecgh/go-spew/spew/dump_test.go +++ b/vendor/github.com/davecgh/go-spew/spew/dump_test.go @@ -768,7 +768,7 @@ func addUintptrDumpTests() { func addUnsafePointerDumpTests() { // Null pointer. - v := unsafe.Pointer(uintptr(0)) + v := unsafe.Pointer(nil) nv := (*unsafe.Pointer)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) diff --git a/vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go b/vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go index 6ab1808..108baa5 100644 --- a/vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go +++ b/vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go @@ -82,18 +82,20 @@ func addCgoDumpTests() { v5Len := fmt.Sprintf("%d", v5l) v5Cap := fmt.Sprintf("%d", v5c) v5t := "[6]testdata._Ctype_uint8_t" + v5t2 := "[6]testdata._Ctype_uchar" v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " + "{\n 00000000 74 65 73 74 35 00 " + " |test5.|\n}" - addDumpTest(v5, "("+v5t+") "+v5s+"\n") + addDumpTest(v5, "("+v5t+") "+v5s+"\n", "("+v5t2+") "+v5s+"\n") // C typedefed unsigned char array. v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray() v6Len := fmt.Sprintf("%d", v6l) v6Cap := fmt.Sprintf("%d", v6c) v6t := "[6]testdata._Ctype_custom_uchar_t" + v6t2 := "[6]testdata._Ctype_uchar" v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " + "{\n 00000000 74 65 73 74 36 00 " + " |test6.|\n}" - addDumpTest(v6, "("+v6t+") "+v6s+"\n") + addDumpTest(v6, "("+v6t+") "+v6s+"\n", "("+v6t2+") "+v6s+"\n") } diff --git a/vendor/github.com/davecgh/go-spew/spew/format.go b/vendor/github.com/davecgh/go-spew/spew/format.go index c49875b..b04edb7 100644 --- a/vendor/github.com/davecgh/go-spew/spew/format.go +++ b/vendor/github.com/davecgh/go-spew/spew/format.go @@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) { // Display dereferenced value. switch { - case nilFound == true: + case nilFound: f.fs.Write(nilAngleBytes) - case cycleFound == true: + case cycleFound: f.fs.Write(circularShortBytes) default: diff --git a/vendor/github.com/davecgh/go-spew/spew/format_test.go b/vendor/github.com/davecgh/go-spew/spew/format_test.go index f9b93ab..87ee965 100644 --- a/vendor/github.com/davecgh/go-spew/spew/format_test.go +++ b/vendor/github.com/davecgh/go-spew/spew/format_test.go @@ -1083,7 +1083,7 @@ func addUintptrFormatterTests() { func addUnsafePointerFormatterTests() { // Null pointer. - v := unsafe.Pointer(uintptr(0)) + v := unsafe.Pointer(nil) nv := (*unsafe.Pointer)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) @@ -1536,14 +1536,14 @@ func TestPrintSortedKeys(t *testing.T) { t.Errorf("Sorted keys mismatch 3:\n %v %v", s, expected) } - s = cfg.Sprint(map[testStruct]int{testStruct{1}: 1, testStruct{3}: 3, testStruct{2}: 2}) + s = cfg.Sprint(map[testStruct]int{{1}: 1, {3}: 3, {2}: 2}) expected = "map[ts.1:1 ts.2:2 ts.3:3]" if s != expected { t.Errorf("Sorted keys mismatch 4:\n %v %v", s, expected) } if !spew.UnsafeDisabled { - s = cfg.Sprint(map[testStructP]int{testStructP{1}: 1, testStructP{3}: 3, testStructP{2}: 2}) + s = cfg.Sprint(map[testStructP]int{{1}: 1, {3}: 3, {2}: 2}) expected = "map[ts.1:1 ts.2:2 ts.3:3]" if s != expected { t.Errorf("Sorted keys mismatch 5:\n %v %v", s, expected) diff --git a/vendor/github.com/davecgh/go-spew/spew/internal_test.go b/vendor/github.com/davecgh/go-spew/spew/internal_test.go index 20a9cfe..e312b4f 100644 --- a/vendor/github.com/davecgh/go-spew/spew/internal_test.go +++ b/vendor/github.com/davecgh/go-spew/spew/internal_test.go @@ -36,10 +36,7 @@ type dummyFmtState struct { } func (dfs *dummyFmtState) Flag(f int) bool { - if f == int('+') { - return true - } - return false + return f == int('+') } func (dfs *dummyFmtState) Precision() (int, bool) { diff --git a/vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go b/vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go index a0c612e..80dc221 100644 --- a/vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go +++ b/vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go @@ -16,7 +16,7 @@ // when the code is not running on Google App Engine, compiled by GopherJS, and // "-tags safe" is not added to the go build command line. The "disableunsafe" // tag is deprecated and thus should not be used. -// +build !js,!appengine,!safe,!disableunsafe +// +build !js,!appengine,!safe,!disableunsafe,go1.4 /* This test file is part of the spew package rather than than the spew_test @@ -30,7 +30,6 @@ import ( "bytes" "reflect" "testing" - "unsafe" ) // changeKind uses unsafe to intentionally change the kind of a reflect.Value to @@ -38,13 +37,13 @@ import ( // fallback code which punts to the standard fmt library for new types that // might get added to the language. func changeKind(v *reflect.Value, readOnly bool) { - rvf := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + offsetFlag)) - *rvf = *rvf | ((1< 23 { + fmt.Fprintf(os.Stderr, "Invalid flag 'pcr': value %d is out of range", *pcr) + os.Exit(1) + } + + err := run(*pcr, *tpmPath) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } +} + +func run(pcr int, tpmPath string) (retErr error) { + // Open the TPM + rwc, err := tpm2.OpenTPM(tpmPath) + if err != nil { + return fmt.Errorf("can't open TPM %q: %v", tpmPath, err) + } + defer func() { + if err := rwc.Close(); err != nil { + retErr = fmt.Errorf("%v\ncan't close TPM %q: %v", retErr, tpmPath, err) + } + }() + + // Create the parent key against which to seal the data + srkPassword := "" + srkHandle, _, err := tpm2.CreatePrimary(rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", srkPassword, srkTemplate) + if err != nil { + return fmt.Errorf("can't create primary key: %v", err) + } + defer func() { + if err := tpm2.FlushContext(rwc, srkHandle); err != nil { + retErr = fmt.Errorf("%v\nunable to flush SRK handle %q: %v", retErr, srkHandle, err) + } + }() + fmt.Printf("Created parent key with handle: 0x%x\n", srkHandle) + + // Note the value of the pcr against which we will seal the data + pcrVal, err := tpm2.ReadPCR(rwc, pcr, tpm2.AlgSHA256) + if err != nil { + return fmt.Errorf("unable to read PCR: %v", err) + } + fmt.Printf("PCR %v value: 0x%x\n", pcr, pcrVal) + + // Get the authorization policy that will protect the data to be sealed + objectPassword := "objectPassword" + sessHandle, policy, err := policyPCRPasswordSession(rwc, pcr, objectPassword) + if err != nil { + return fmt.Errorf("unable to get policy: %v", err) + } + if err := tpm2.FlushContext(rwc, sessHandle); err != nil { + return fmt.Errorf("unable to flush session: %v", err) + } + fmt.Printf("Created authorization policy: 0x%x\n", policy) + + // Seal the data to the parent key and the policy + dataToSeal := []byte("secret") + fmt.Printf("Data to be sealed: 0x%x\n", dataToSeal) + privateArea, publicArea, err := tpm2.Seal(rwc, srkHandle, srkPassword, objectPassword, policy, dataToSeal) + if err != nil { + return fmt.Errorf("unable to seal data: %v", err) + } + fmt.Printf("Sealed data: 0x%x\n", privateArea) + + // Load the sealed data into the TPM. + objectHandle, _, err := tpm2.Load(rwc, srkHandle, srkPassword, publicArea, privateArea) + if err != nil { + return fmt.Errorf("unable to load data: %v", err) + } + defer func() { + if err := tpm2.FlushContext(rwc, objectHandle); err != nil { + retErr = fmt.Errorf("%v\nunable to flush object handle %q: %v", retErr, objectHandle, err) + } + }() + fmt.Printf("Loaded sealed data with handle: 0x%x\n", objectHandle) + + // Unseal the data + unsealedData, err := unseal(rwc, pcr, objectPassword, objectHandle) + if err != nil { + return fmt.Errorf("unable to unseal data: %v", err) + } + fmt.Printf("Unsealed data: 0x%x\n", unsealedData) + + // Try to unseal the data with the wrong password + _, err = unseal(rwc, pcr, "wrong-password", objectHandle) + fmt.Printf("Trying to unseal with wrong password resulted in: %v\n", err) + + // Extend the PCR + if err := tpm2.PCREvent(rwc, tpmutil.Handle(pcr), []byte{1}); err != nil { + return fmt.Errorf("unable to extend PCR: %v", err) + } + fmt.Printf("Extended PCR %d\n", pcr) + + // Note the new value of the pcr + pcrVal, err = tpm2.ReadPCR(rwc, pcr, tpm2.AlgSHA256) + if err != nil { + return fmt.Errorf("unable to read PCR: %v", err) + } + fmt.Printf("PCR %d value: 0x%x\n", pcr, pcrVal) + + // Try to unseal the data with the PCR in the wrong state + _, err = unseal(rwc, pcr, objectPassword, objectHandle) + fmt.Printf("Trying to unseal with wrong PCR state resulted in: %v\n", err) + + return +} + +// Returns the unsealed data +func unseal(rwc io.ReadWriteCloser, pcr int, objectPassword string, objectHandle tpmutil.Handle) (data []byte, retErr error) { + // Create the authorization session + sessHandle, _, err := policyPCRPasswordSession(rwc, pcr, objectPassword) + if err != nil { + return nil, fmt.Errorf("unable to get auth session: %v", err) + } + defer func() { + if err := tpm2.FlushContext(rwc, sessHandle); err != nil { + retErr = fmt.Errorf("%v\nunable to flush session: %v", retErr, err) + } + }() + + // Unseal the data + unsealedData, err := tpm2.UnsealWithSession(rwc, sessHandle, objectHandle, objectPassword) + if err != nil { + return nil, fmt.Errorf("unable to unseal data: %v", err) + } + return unsealedData, nil +} + +// Returns session handle and policy digest. +func policyPCRPasswordSession(rwc io.ReadWriteCloser, pcr int, password string) (sessHandle tpmutil.Handle, policy []byte, retErr error) { + // FYI, this is not a very secure session. + sessHandle, _, err := tpm2.StartAuthSession( + rwc, + tpm2.HandleNull, /*tpmKey*/ + tpm2.HandleNull, /*bindKey*/ + make([]byte, 16), /*nonceCaller*/ + nil, /*secret*/ + tpm2.SessionPolicy, + tpm2.AlgNull, + tpm2.AlgSHA256) + if err != nil { + return tpm2.HandleNull, nil, fmt.Errorf("unable to start session: %v", err) + } + defer func() { + if sessHandle != tpm2.HandleNull && err != nil { + if err := tpm2.FlushContext(rwc, sessHandle); err != nil { + retErr = fmt.Errorf("%v\nunable to flush session: %v", retErr, err) + } + } + }() + + pcrSelection := tpm2.PCRSelection{ + Hash: tpm2.AlgSHA256, + PCRs: []int{pcr}, + } + + // An empty expected digest means that digest verification is skipped. + if err := tpm2.PolicyPCR(rwc, sessHandle, nil /*expectedDigest*/, pcrSelection); err != nil { + return sessHandle, nil, fmt.Errorf("unable to bind PCRs to auth policy: %v", err) + } + + if err := tpm2.PolicyPassword(rwc, sessHandle); err != nil { + return sessHandle, nil, fmt.Errorf("unable to require password for auth policy: %v", err) + } + + policy, err = tpm2.PolicyGetDigest(rwc, sessHandle) + if err != nil { + return sessHandle, nil, fmt.Errorf("unable to get policy digest: %v", err) + } + return sessHandle, policy, nil +} diff --git a/vendor/github.com/google/go-tpm/go.mod b/vendor/github.com/google/go-tpm/go.mod new file mode 100644 index 0000000..aaa500a --- /dev/null +++ b/vendor/github.com/google/go-tpm/go.mod @@ -0,0 +1,5 @@ +module github.com/google/go-tpm + +go 1.12 + +require github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845 diff --git a/vendor/github.com/google/go-tpm/go.sum b/vendor/github.com/google/go-tpm/go.sum new file mode 100644 index 0000000..46d1051 --- /dev/null +++ b/vendor/github.com/google/go-tpm/go.sum @@ -0,0 +1,35 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI= +github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845 h1:2WNNKKRI+a5OZi5xiJVfDoOiUyfK/BU1D4w+N6967F4= +github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/google/go-tpm/tpm/commands.go b/vendor/github.com/google/go-tpm/tpm/commands.go index cbf6b1c..b9033d4 100644 --- a/vendor/github.com/google/go-tpm/tpm/commands.go +++ b/vendor/github.com/google/go-tpm/tpm/commands.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014, Google Inc. All rights reserved. +// Copyright (c) 2014, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -67,7 +67,7 @@ func osap(rw io.ReadWriter, osap *osapCommand) (*osapResponse, error) { } // seal performs a seal operation on the TPM. -func seal(rw io.ReadWriter, sc *sealCommand, pcrs *pcrInfoLong, data []byte, ca *commandAuth) (*tpmStoredData, *responseAuth, uint32, error) { +func seal(rw io.ReadWriter, sc *sealCommand, pcrs *pcrInfoLong, data tpmutil.U32Bytes, ca *commandAuth) (*tpmStoredData, *responseAuth, uint32, error) { pcrsize := binary.Size(pcrs) if pcrsize < 0 { return nil, nil, 0, errors.New("couldn't compute the size of a pcrInfoLong") @@ -91,7 +91,7 @@ func seal(rw io.ReadWriter, sc *sealCommand, pcrs *pcrInfoLong, data []byte, ca // unseal data sealed by the TPM. func unseal(rw io.ReadWriter, keyHandle tpmutil.Handle, sealed *tpmStoredData, ca1 *commandAuth, ca2 *commandAuth) ([]byte, *responseAuth, *responseAuth, uint32, error) { in := []interface{}{keyHandle, sealed, ca1, ca2} - var outb []byte + var outb tpmutil.U32Bytes var ra1 responseAuth var ra2 responseAuth out := []interface{}{&outb, &ra1, &ra2} @@ -143,6 +143,33 @@ func getPubKey(rw io.ReadWriter, keyHandle tpmutil.Handle, ca *commandAuth) (*pu return &pk, &ra, ret, nil } +// getCapability reads the requested capability and sub-capability from NVRAM +func getCapability(rw io.ReadWriter, cap, subcap uint32) ([]byte, error) { + subCapBytes, err := tpmutil.Pack(subcap) + if err != nil { + return nil, err + } + var b tpmutil.U32Bytes + in := []interface{}{cap, tpmutil.U32Bytes(subCapBytes)} + out := []interface{}{&b} + if _, err := submitTPMRequest(rw, tagRQUCommand, ordGetCapability, in, out); err != nil { + return nil, err + } + return b, nil +} + +func nvReadValue(rw io.ReadWriter, index, offset, len uint32, ca *commandAuth) ([]byte, *responseAuth, uint32, error) { + var b tpmutil.U32Bytes + var ra responseAuth + in := []interface{}{index, offset, len, ca} + out := []interface{}{&b, &ra} + ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordNVReadValue, in, out) + if err != nil { + return nil, nil, 0, err + } + return b, &ra, ret, nil +} + // quote2 signs arbitrary data under a given set of PCRs and using a key // specified by keyHandle. It returns information about the PCRs it signed // under, the signature, auth information, and optionally information about the @@ -152,8 +179,8 @@ func quote2(rw io.ReadWriter, keyHandle tpmutil.Handle, hash [20]byte, pcrs *pcr in := []interface{}{keyHandle, hash, pcrs, addVersion, ca} var pcrShort pcrInfoShort var capInfo capVersionInfo - var capBytes []byte - var sig []byte + var capBytes tpmutil.U32Bytes + var sig tpmutil.U32Bytes var ra responseAuth out := []interface{}{&pcrShort, &capBytes, &sig, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordQuote2, in, out) @@ -162,27 +189,27 @@ func quote2(rw io.ReadWriter, keyHandle tpmutil.Handle, hash [20]byte, pcrs *pcr } // Deserialize the capInfo, if any. - if len(capBytes) == 0 { + if len([]byte(capBytes)) == 0 { return &pcrShort, nil, capBytes, sig, &ra, ret, nil } size := binary.Size(capInfo.CapVersionFixed) - capInfo.VendorSpecific = make([]byte, len(capBytes)-size) - if _, err := tpmutil.Unpack(capBytes[:size], &capInfo.CapVersionFixed); err != nil { + capInfo.VendorSpecific = make([]byte, len([]byte(capBytes))-size) + if _, err := tpmutil.Unpack([]byte(capBytes)[:size], &capInfo.CapVersionFixed); err != nil { return nil, nil, nil, nil, nil, 0, err } - copy(capInfo.VendorSpecific, capBytes[size:]) + copy(capInfo.VendorSpecific, []byte(capBytes)[size:]) return &pcrShort, &capInfo, capBytes, sig, &ra, ret, nil } // quote performs a TPM 1.1 quote operation: it signs data using the -// TPM_QUOTE_INFO structure for the current values of a selectied set of PCRs. +// TPM_QUOTE_INFO structure for the current values of a selected set of PCRs. func quote(rw io.ReadWriter, keyHandle tpmutil.Handle, hash [20]byte, pcrs *pcrSelection, ca *commandAuth) (*pcrComposite, []byte, *responseAuth, uint32, error) { in := []interface{}{keyHandle, hash, pcrs, ca} var pcrc pcrComposite - var sig []byte + var sig tpmutil.U32Bytes var ra responseAuth out := []interface{}{&pcrc, &sig, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordQuote, in, out) @@ -198,7 +225,7 @@ func quote(rw io.ReadWriter, keyHandle tpmutil.Handle, hash [20]byte, pcrs *pcrS func makeIdentity(rw io.ReadWriter, encAuth digest, idDigest digest, k *key, ca1 *commandAuth, ca2 *commandAuth) (*key, []byte, *responseAuth, *responseAuth, uint32, error) { in := []interface{}{encAuth, idDigest, k, ca1, ca2} var aik key - var sig []byte + var sig tpmutil.U32Bytes var ra1 responseAuth var ra2 responseAuth out := []interface{}{&aik, &sig, &ra1, &ra2} @@ -210,6 +237,22 @@ func makeIdentity(rw io.ReadWriter, encAuth digest, idDigest digest, k *key, ca1 return &aik, sig, &ra1, &ra2, ret, nil } +// activateIdentity provides the TPM with an EK encrypted challenge and asks it to +// decrypt the challenge and return the secret (symmetric key). +func activateIdentity(rw io.ReadWriter, keyHandle tpmutil.Handle, blob tpmutil.U32Bytes, ca1 *commandAuth, ca2 *commandAuth) (*symKey, *responseAuth, *responseAuth, uint32, error) { + in := []interface{}{keyHandle, blob, ca1, ca2} + var symkey symKey + var ra1 responseAuth + var ra2 responseAuth + out := []interface{}{&symkey, &ra1, &ra2} + ret, err := submitTPMRequest(rw, tagRQUAuth2Command, ordActivateIdentity, in, out) + if err != nil { + return nil, nil, nil, 0, err + } + + return &symkey, &ra1, &ra2, ret, nil +} + // resetLockValue resets the dictionary-attack lock in the TPM, using owner // auth. func resetLockValue(rw io.ReadWriter, ca *commandAuth) (*responseAuth, uint32, error) { @@ -274,7 +317,7 @@ func ownerClear(rw io.ReadWriter, ca *commandAuth) (*responseAuth, uint32, error // owner auth. This operation can only be performed if there is no owner. The // TPM can be put into this state using TPM_OwnerClear. The encOwnerAuth and // encSRKAuth values must be encrypted using the endorsement key. -func takeOwnership(rw io.ReadWriter, encOwnerAuth []byte, encSRKAuth []byte, srk *key, ca *commandAuth) (*key, *responseAuth, uint32, error) { +func takeOwnership(rw io.ReadWriter, encOwnerAuth tpmutil.U32Bytes, encSRKAuth tpmutil.U32Bytes, srk *key, ca *commandAuth) (*key, *responseAuth, uint32, error) { in := []interface{}{pidOwner, encOwnerAuth, encSRKAuth, srk, ca} var k key var ra responseAuth @@ -301,9 +344,9 @@ func createWrapKey(rw io.ReadWriter, encUsageAuth digest, encMigrationAuth diges return &k, &ra, ret, nil } -func sign(rw io.ReadWriter, keyHandle tpmutil.Handle, data []byte, ca *commandAuth) ([]byte, *responseAuth, uint32, error) { +func sign(rw io.ReadWriter, keyHandle tpmutil.Handle, data tpmutil.U32Bytes, ca *commandAuth) ([]byte, *responseAuth, uint32, error) { in := []interface{}{keyHandle, data, ca} - var signature []byte + var signature tpmutil.U32Bytes var ra responseAuth out := []interface{}{&signature, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordSign, in, out) diff --git a/vendor/github.com/google/go-tpm/tpm/constants.go b/vendor/github.com/google/go-tpm/tpm/constants.go index 7dd7704..3b5b4f5 100644 --- a/vendor/github.com/google/go-tpm/tpm/constants.go +++ b/vendor/github.com/google/go-tpm/tpm/constants.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014, Google Inc. All rights reserved. +// Copyright (c) 2014, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,11 +16,6 @@ package tpm import "github.com/google/go-tpm/tpmutil" -func init() { - // TPM 1.2 spec uses uint32 for length prefix of byte arrays. - tpmutil.UseTPM12LengthPrefixSize() -} - // Supported TPM commands. const ( tagPCRInfoLong uint16 = 0x06 @@ -53,15 +48,23 @@ const ( ordForceClear uint32 = 0x0000005D ordGetCapability uint32 = 0x00000065 ordMakeIdentity uint32 = 0x00000079 + ordActivateIdentity uint32 = 0x0000007A ordReadPubEK uint32 = 0x0000007C ordOwnerReadInternalPub uint32 = 0x00000081 ordFlushSpecific uint32 = 0x000000BA ordPcrReset uint32 = 0x000000C8 + ordNVReadValue uint32 = 0x000000CF ) // Capability types. const ( - capHandle uint32 = 0x00000014 + capProperty uint32 = 0x00000005 + capHandle uint32 = 0x00000014 +) + +// SubCapabilities +const ( + tpmCapPropManufacturer uint32 = 0x00000103 ) // Entity types. The LSB gives the entity type, and the MSB (currently fixed to @@ -130,6 +133,7 @@ const ( esRSAEsOAEPSHA1MGF1 esSymCTR esSymOFB + esSymCBCPKCS5 = 0xff // esSymCBCPKCS5 was taken from go-tspi ) // Signature schemes. These are only valid under algRSA. diff --git a/vendor/github.com/google/go-tpm/tpm/errors.go b/vendor/github.com/google/go-tpm/tpm/errors.go index 18b7a9f..3877cb4 100644 --- a/vendor/github.com/google/go-tpm/tpm/errors.go +++ b/vendor/github.com/google/go-tpm/tpm/errors.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014, Google Inc. All rights reserved. +// Copyright (c) 2014, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -82,7 +82,7 @@ const ( errNoWrapTransport errAuditFailUnsuccessful errAuditFailSuccessful - errNotResetable + errNotResettable errNotLocal errBadType errInvalidResource @@ -108,7 +108,7 @@ const ( errNoOperator errResourceMissing errDelegateLock - errDelegateFamliy + errDelegateFamily errDelegateAdmin errTransportNotExclusive errOwnerControl @@ -118,7 +118,7 @@ const ( errDAAIssuerSettings errDAASettings errDAAState - errDAAIssuerVailidity + errDAAIssuerValidity errDAAWrongW errBadHandle errBadDelegate @@ -184,7 +184,7 @@ var tpmErrMsgs = map[tpmError]string{ errNoWrapTransport: "the TPM does not allow for wrapped transport sessions", errAuditFailUnsuccessful: "TPM audit construction failed and the underlying command was returning a failure code also", errAuditFailSuccessful: "TPM audit construction failed and the underlying command was returning success", - errNotResetable: "attempt to reset a PCR register that does not have the resettable attribute", + errNotResettable: "attempt to reset a PCR register that does not have the resettable attribute", errNotLocal: "attempt to reset a PCR register that requires locality and locality modifier not part of command transport", errBadType: "make identity blob not properly typed", errInvalidResource: "when saving context identified resource type does not match actual resource", @@ -210,7 +210,7 @@ var tpmErrMsgs = map[tpmError]string{ errNoOperator: "no operator AuthData value is set", errResourceMissing: "the resource pointed to by context is not loaded", errDelegateLock: "the delegate administration is locked", - errDelegateFamliy: "attempt to manage a family other than the delegated family", + errDelegateFamily: "attempt to manage a family other than the delegated family", errDelegateAdmin: "delegation table management not enabled", errTransportNotExclusive: "there was a command executed outside of an exclusive transport session", errOwnerControl: "attempt to context save a owner evict controlled key", @@ -220,7 +220,7 @@ var tpmErrMsgs = map[tpmError]string{ errDAAIssuerSettings: "the consistency check on DAA_issuerSettings has failed", errDAASettings: "the consistency check on DAA_tpmSpecific has failed", errDAAState: "the atomic process indicated by the submitted DAA command is not the expected process", - errDAAIssuerVailidity: "the issuer's validity check has detected an inconsistency", + errDAAIssuerValidity: "the issuer's validity check has detected an inconsistency", errDAAWrongW: "the consistency check on w has failed", errBadHandle: "the handle is incorrect", errBadDelegate: "delegation is not correct", diff --git a/vendor/github.com/google/go-tpm/tpmutil/run_linux.go b/vendor/github.com/google/go-tpm/tpm/open_other.go similarity index 59% rename from vendor/github.com/google/go-tpm/tpmutil/run_linux.go rename to vendor/github.com/google/go-tpm/tpm/open_other.go index 9056f3f..e39558b 100644 --- a/vendor/github.com/google/go-tpm/tpmutil/run_linux.go +++ b/vendor/github.com/google/go-tpm/tpm/open_other.go @@ -1,4 +1,6 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// +build !windows + +// Copyright (c) 2019, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,42 +14,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -package tpmutil +package tpm import ( "fmt" "io" - "net" - "os" + + "github.com/google/go-tpm/tpmutil" ) // OpenTPM opens a channel to the TPM at the given path. If the file is a // device, then it treats it like a normal TPM device, and if the file is a // Unix domain socket, then it opens a connection to the socket. func OpenTPM(path string) (io.ReadWriteCloser, error) { - // If it's a regular file, then open it - var rwc io.ReadWriteCloser - fi, err := os.Stat(path) + rwc, err := tpmutil.OpenTPM(path) if err != nil { return nil, err } - if fi.Mode()&os.ModeDevice != 0 { - var f *os.File - f, err = os.OpenFile(path, os.O_RDWR, 0600) - if err != nil { - return nil, err - } - rwc = io.ReadWriteCloser(f) - } else if fi.Mode()&os.ModeSocket != 0 { - uc, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: path, Net: "unix"}) - if err != nil { - return nil, err - } - rwc = io.ReadWriteCloser(uc) - } else { - return nil, fmt.Errorf("unsupported TPM file mode %s", fi.Mode().String()) + // Make sure this is a TPM 1.2 + _, err = GetManufacturer(rwc) + if err != nil { + rwc.Close() + return nil, fmt.Errorf("open %s: device is not a TPM 1.2", path) } - return rwc, nil } diff --git a/vendor/github.com/google/go-tpm/tpm/open_windows.go b/vendor/github.com/google/go-tpm/tpm/open_windows.go new file mode 100644 index 0000000..5032a2a --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm/open_windows.go @@ -0,0 +1,37 @@ +// Copyright (c) 2018, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tpm + +import ( + "fmt" + "io" + + "github.com/google/go-tpm/tpmutil" + "github.com/google/go-tpm/tpmutil/tbs" +) + +// OpenTPM opens a channel to the TPM. +func OpenTPM() (io.ReadWriteCloser, error) { + info, err := tbs.GetDeviceInfo() + if err != nil { + return nil, err + } + + if info.TPMVersion != tbs.TPMVersion12 { + return nil, fmt.Errorf("openTPM: device is not a TPM 1.2") + } + + return tpmutil.OpenTPM() +} diff --git a/vendor/github.com/google/go-tpm/tpm/pcrs.go b/vendor/github.com/google/go-tpm/tpm/pcrs.go index bcd34be..77fe4fb 100644 --- a/vendor/github.com/google/go-tpm/tpm/pcrs.go +++ b/vendor/github.com/google/go-tpm/tpm/pcrs.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014, Google Inc. All rights reserved. +// Copyright (c) 2014, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/google/go-tpm/tpm/pcrs_test.go b/vendor/github.com/google/go-tpm/tpm/pcrs_test.go index 2cfe72d..534ae54 100644 --- a/vendor/github.com/google/go-tpm/tpm/pcrs_test.go +++ b/vendor/github.com/google/go-tpm/tpm/pcrs_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014, Google Inc. All rights reserved. +// Copyright (c) 2014, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/google/go-tpm/tpm/structures.go b/vendor/github.com/google/go-tpm/tpm/structures.go index 2944c92..98be26f 100644 --- a/vendor/github.com/google/go-tpm/tpm/structures.go +++ b/vendor/github.com/google/go-tpm/tpm/structures.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014, Google Inc. All rights reserved. +// Copyright (c) 2014, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -69,7 +69,7 @@ type pcrInfo struct { // A capVersionInfo contains information about the TPM itself. Note that this // is deserialized specially, since it has a variable-length byte array but no -// length. It is preceeded with a length in the response to the Quote2 command. +// length. It is preceded with a length in the response to the Quote2 command. type capVersionInfo struct { CapVersionFixed capVersionInfoFixed VendorSpecific []byte @@ -188,64 +188,71 @@ func (ra responseAuth) String() string { } // These are the parameters of a TPM key. -type keyParms struct { +type keyParams struct { AlgID uint32 EncScheme uint16 SigScheme uint16 - Parms []byte // Serialized rsaKeyParms or symmetricKeyParms. + Params tpmutil.U32Bytes // Serialized rsaKeyParams or symmetricKeyParams. } -// An rsaKeyParms encodes the length of the RSA prime in bits, the number of +// An rsaKeyParams encodes the length of the RSA prime in bits, the number of // primes in its factored form, and the exponent used for public-key // encryption. -type rsaKeyParms struct { +type rsaKeyParams struct { KeyLength uint32 NumPrimes uint32 - Exponent []byte + Exponent tpmutil.U32Bytes } -type symmetricKeyParms struct { +type symmetricKeyParams struct { KeyLength uint32 BlockSize uint32 - IV []byte + IV tpmutil.U32Bytes } // A key is a TPM representation of a key. type key struct { - Version uint32 - KeyUsage uint16 - KeyFlags uint32 - AuthDataUsage byte - AlgorithmParms keyParms - PCRInfo []byte - PubKey []byte - EncData []byte + Version uint32 + KeyUsage uint16 + KeyFlags uint32 + AuthDataUsage byte + AlgorithmParams keyParams + PCRInfo tpmutil.U32Bytes + PubKey tpmutil.U32Bytes + EncData tpmutil.U32Bytes } // A key12 is a newer TPM representation of a key. type key12 struct { - Tag uint16 - Zero uint16 // Always all 0. - KeyUsage uint16 - KeyFlags uint32 - AuthDataUsage byte - AlgorithmParms keyParms - PCRInfo []byte // This must be a serialization of a pcrInfoLong. - PubKey []byte - EncData []byte + Tag uint16 + Zero uint16 // Always all 0. + KeyUsage uint16 + KeyFlags uint32 + AuthDataUsage byte + AlgorithmParams keyParams + PCRInfo tpmutil.U32Bytes // This must be a serialization of a pcrInfoLong. + PubKey tpmutil.U32Bytes + EncData tpmutil.U32Bytes } // A pubKey represents a public key known to the TPM. type pubKey struct { - AlgorithmParms keyParms - Key []byte + AlgorithmParams keyParams + Key tpmutil.U32Bytes +} + +// A symKey is a TPM representation of a symmetric key. +type symKey struct { + AlgID uint32 + EncScheme uint16 + Key tpmutil.U16Bytes // TPM_SYMMETRIC_KEY uses a 16-bit header for Key data } // A tpmStoredData holds sealed data from the TPM. type tpmStoredData struct { Version uint32 - Info []byte - Enc []byte + Info tpmutil.U32Bytes + Enc tpmutil.U32Bytes } // String returns a string representation of a tpmStoredData. @@ -271,7 +278,7 @@ type quoteInfo struct { // A pcrComposite stores a selection of PCRs with the selected PCR values. type pcrComposite struct { Selection pcrSelection - Values []byte + Values tpmutil.U32Bytes } // convertPubKey converts a public key into TPM form. Currently, this function @@ -285,7 +292,7 @@ func convertPubKey(pk crypto.PublicKey) (*pubKey, error) { return nil, errors.New("The provided Privacy CA RSA public key was not a 2048-bit key") } - rsakp := rsaKeyParms{ + rsakp := rsaKeyParams{ KeyLength: 2048, NumPrimes: 2, Exponent: big.NewInt(int64(pkRSA.E)).Bytes(), @@ -294,16 +301,16 @@ func convertPubKey(pk crypto.PublicKey) (*pubKey, error) { if err != nil { return nil, err } - kp := keyParms{ + kp := keyParams{ AlgID: algRSA, EncScheme: esNone, SigScheme: ssRSASaPKCS1v15SHA1, - Parms: rsakpb, + Params: rsakpb, } - pubk := pubKey{ - AlgorithmParms: kp, - Key: pkRSA.N.Bytes(), + pubKey := pubKey{ + AlgorithmParams: kp, + Key: pkRSA.N.Bytes(), } - return &pubk, nil + return &pubKey, nil } diff --git a/vendor/github.com/google/go-tpm/tpm/tpm.go b/vendor/github.com/google/go-tpm/tpm/tpm.go index a6bb12a..fb8dcc5 100644 --- a/vendor/github.com/google/go-tpm/tpm/tpm.go +++ b/vendor/github.com/google/go-tpm/tpm/tpm.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014, Google Inc. All rights reserved. +// Copyright (c) 2014, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,34 +18,26 @@ package tpm import ( "bytes" "crypto" + "crypto/aes" + "crypto/cipher" "crypto/hmac" "crypto/rand" "crypto/rsa" "crypto/sha1" "encoding/binary" "errors" + "fmt" "io" "github.com/google/go-tpm/tpmutil" ) -// OpenTPM opens a channel to the TPM at the given path. If the file is a -// device, then it treats it like a normal TPM device, and if the file is a -// Unix domain socket, then it opens a connection to the socket. -var OpenTPM = tpmutil.OpenTPM - // GetKeys gets the list of handles for currently-loaded TPM keys. func GetKeys(rw io.ReadWriter) ([]tpmutil.Handle, error) { - var b []byte - subCap, err := tpmutil.Pack(rtKey) + b, err := getCapability(rw, capHandle, rtKey) if err != nil { return nil, err } - in := []interface{}{capHandle, subCap} - out := []interface{}{&b} - if _, err := submitTPMRequest(rw, tagRQUCommand, ordGetCapability, in, out); err != nil { - return nil, err - } var handles []tpmutil.Handle if _, err := tpmutil.Unpack(b, &handles); err != nil { return nil, err @@ -96,7 +88,7 @@ func FetchPCRValues(rw io.ReadWriter, pcrVals []int) ([]byte, error) { // GetRandom gets random bytes from the TPM. func GetRandom(rw io.ReadWriter, size uint32) ([]byte, error) { - var b []byte + var b tpmutil.U32Bytes in := []interface{}{size} out := []interface{}{&b} // There's no need to check the ret value here, since the err value @@ -180,7 +172,7 @@ func Quote2(rw io.ReadWriter, handle tpmutil.Handle, data []byte, pcrVals []int, } // Check response authentication. - raIn := []interface{}{ret, ordQuote2, pcrShort, capBytes, sig} + raIn := []interface{}{ret, ordQuote2, pcrShort, tpmutil.U32Bytes(capBytes), tpmutil.U32Bytes(sig)} if err := ra.verify(ca.NonceOdd, sharedSecret[:], raIn); err != nil { return nil, err } @@ -257,7 +249,7 @@ func newOSAPSession(rw io.ReadWriter, entityType uint16, entityValue tpmutil.Han hm.Write(osapData) // Note that crypto/hash.Sum returns a slice rather than an array, so we // have to copy this into an array to make sure that serialization doesn't - // preprend a length in tpmutil.Pack(). + // prepend a length in tpmutil.Pack(). sharedSecretBytes := hm.Sum(nil) copy(sharedSecret[:], sharedSecretBytes) return sharedSecret, osapr, nil @@ -356,7 +348,7 @@ func sealHelper(rw io.ReadWriter, pcrInfo *pcrInfoLong, data []byte, srkAuth []b // digest = SHA1(ordSeal || encAuth || binary.Size(pcrInfo) || pcrInfo || // len(data) || data) // - authIn := []interface{}{ordSeal, sc.EncAuth, uint32(binary.Size(pcrInfo)), pcrInfo, data} + authIn := []interface{}{ordSeal, sc.EncAuth, uint32(binary.Size(pcrInfo)), pcrInfo, tpmutil.U32Bytes(data)} ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, sharedSecret[:], authIn) if err != nil { return nil, err @@ -449,7 +441,7 @@ func Unseal(rw io.ReadWriter, sealed []byte, srkAuth []byte) ([]byte, error) { } // Check the response authentication. - raIn := []interface{}{ret, ordUnseal, unsealed} + raIn := []interface{}{ret, ordUnseal, tpmutil.U32Bytes(unsealed)} if err := ra1.verify(ca1.NonceOdd, sharedSecret[:], raIn); err != nil { return nil, err } @@ -491,7 +483,7 @@ func Quote(rw io.ReadWriter, handle tpmutil.Handle, data []byte, pcrNums []int, } // Check response authentication. - raIn := []interface{}{ret, ordQuote, pcrc, sig} + raIn := []interface{}{ret, ordQuote, pcrc, tpmutil.U32Bytes(sig)} if err := ra.verify(ca.NonceOdd, sharedSecret[:], raIn); err != nil { return nil, nil, err } @@ -548,14 +540,14 @@ func MakeIdentity(rw io.ReadWriter, srkAuth []byte, ownerAuth []byte, aikAuth [] } if pk != nil { - pubk, err := convertPubKey(pk) + pubKey, err := convertPubKey(pk) if err != nil { return nil, err } // We can't pack the pair of values directly, since the label is // included directly as bytes, without any length. - fullpkb, err := tpmutil.Pack(pubk) + fullpkb, err := tpmutil.Pack(pubKey) if err != nil { return nil, err } @@ -564,29 +556,29 @@ func MakeIdentity(rw io.ReadWriter, srkAuth []byte, ownerAuth []byte, aikAuth [] caDigest = sha1.Sum(caDigestBytes) } - rsaAIKParms := rsaKeyParms{ + rsaAIKParams := rsaKeyParams{ KeyLength: 2048, NumPrimes: 2, //Exponent: big.NewInt(0x10001).Bytes(), // 65537. Implicit? } - packedParms, err := tpmutil.Pack(rsaAIKParms) + packedParams, err := tpmutil.Pack(rsaAIKParams) if err != nil { return nil, err } - aikParms := keyParms{ + aikParams := keyParams{ AlgID: algRSA, EncScheme: esNone, SigScheme: ssRSASaPKCS1v15SHA1, - Parms: packedParms, + Params: packedParams, } aik := &key{ - Version: 0x01010000, - KeyUsage: keyIdentity, - KeyFlags: 0, - AuthDataUsage: authAlways, - AlgorithmParms: aikParms, + Version: 0x01010000, + KeyUsage: keyIdentity, + KeyFlags: 0, + AuthDataUsage: authAlways, + AlgorithmParams: aikParams, } // The digest input for MakeIdentity authentication is @@ -610,7 +602,7 @@ func MakeIdentity(rw io.ReadWriter, srkAuth []byte, ownerAuth []byte, aikAuth [] } // Check response authentication. - raIn := []interface{}{ret, ordMakeIdentity, k, sig} + raIn := []interface{}{ret, ordMakeIdentity, k, tpmutil.U32Bytes(sig)} if err := ra1.verify(ca1.NonceOdd, sharedSecretSRK[:], raIn); err != nil { return nil, err } @@ -619,7 +611,7 @@ func MakeIdentity(rw io.ReadWriter, srkAuth []byte, ownerAuth []byte, aikAuth [] return nil, err } - // TODO(tmroeder): check the signature against the pubek. + // TODO(tmroeder): check the signature against the pubEK. blob, err := tpmutil.Pack(k) if err != nil { return nil, err @@ -628,6 +620,124 @@ func MakeIdentity(rw io.ReadWriter, srkAuth []byte, ownerAuth []byte, aikAuth [] return blob, nil } +func unloadTrspiCred(blob []byte) ([]byte, error) { + /* + * Trousers expects the asym blob to have an additional data in the header. + * The relevant data is duplicated in the TPM_SYMMETRIC_KEY struct so we parse + * and throw the header away. + * TODO(dkarch): Trousers is not doing credential activation correctly. We should + * remove this and instead expose the asymmetric decryption and symmetric decryption + * so that anyone generating a challenge for Trousers can unload the header themselves + * and send us a correctly formatted challenge. + */ + + var header struct { + Credsize uint32 + AlgID uint32 + EncScheme uint16 + SigScheme uint16 + Parmsize uint32 + } + + symbuf := bytes.NewReader(blob) + if err := binary.Read(symbuf, binary.BigEndian, &header); err != nil { + return nil, err + } + // Unload the symmetric key parameters. + parms := make([]byte, header.Parmsize) + if err := binary.Read(symbuf, binary.BigEndian, parms); err != nil { + return nil, err + } + // Unload and return the symmetrically encrypted secret. + cred := make([]byte, header.Credsize) + if err := binary.Read(symbuf, binary.BigEndian, cred); err != nil { + return nil, err + } + return cred, nil +} + +// ActivateIdentity asks the TPM to decrypt an EKPub encrypted symmetric session key +// which it uses to decrypt the symmetrically encrypted secret. +func ActivateIdentity(rw io.ReadWriter, aikAuth []byte, ownerAuth []byte, aik tpmutil.Handle, asym, sym []byte) ([]byte, error) { + // Run OIAP for the AIK. + oiaprAIK, err := oiap(rw) + if err != nil { + return nil, fmt.Errorf("failed to start OIAP session: %v", err) + } + + // Run OSAP for the owner, reading a random OddOSAP for our initial command + // and getting back a secret and a handle. + sharedSecretOwn, osaprOwn, err := newOSAPSession(rw, etOwner, khOwner, ownerAuth) + if err != nil { + return nil, fmt.Errorf("failed to start OSAP session: %v", err) + } + defer osaprOwn.Close(rw) + defer zeroBytes(sharedSecretOwn[:]) + + authIn := []interface{}{ordActivateIdentity, tpmutil.U32Bytes(asym)} + ca1, err := newCommandAuth(oiaprAIK.AuthHandle, oiaprAIK.NonceEven, aikAuth, authIn) + if err != nil { + return nil, fmt.Errorf("newCommandAuth failed: %v", err) + } + ca2, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, sharedSecretOwn[:], authIn) + if err != nil { + return nil, fmt.Errorf("newCommandAuth failed: %v", err) + } + + symkey, ra1, ra2, ret, err := activateIdentity(rw, aik, asym, ca1, ca2) + if err != nil { + return nil, fmt.Errorf("activateIdentity failed: %v", err) + } + + // Check response authentication. + raIn := []interface{}{ret, ordActivateIdentity, symkey} + if err := ra1.verify(ca1.NonceOdd, aikAuth, raIn); err != nil { + return nil, fmt.Errorf("aik resAuth failed to verify: %v", err) + } + + if err := ra2.verify(ca2.NonceOdd, sharedSecretOwn[:], raIn); err != nil { + return nil, fmt.Errorf("owner resAuth failed to verify: %v", err) + } + + cred, err := unloadTrspiCred(sym) + var ( + block cipher.Block + iv []byte + ciphertxt []byte + secret []byte + ) + switch id := symkey.AlgID; id { + case algAES128: + block, err = aes.NewCipher(symkey.Key) + if err != nil { + return nil, fmt.Errorf("aes.NewCipher failed: %v", err) + } + iv = cred[:aes.BlockSize] + ciphertxt = cred[aes.BlockSize:] + secret = ciphertxt + default: + return nil, fmt.Errorf("%v is not a supported session key algorithm", id) + } + switch es := symkey.EncScheme; es { + case esSymCTR: + stream := cipher.NewCTR(block, iv) + stream.XORKeyStream(secret, ciphertxt) + case esSymOFB: + stream := cipher.NewOFB(block, iv) + stream.XORKeyStream(secret, ciphertxt) + case esSymCBCPKCS5: + mode := cipher.NewCBCDecrypter(block, iv) + mode.CryptBlocks(secret, ciphertxt) + // Remove PKCS5 padding. + padlen := int(secret[len(secret)-1]) + secret = secret[:len(secret)-padlen] + default: + return nil, fmt.Errorf("%v is not a supported encryption scheme", es) + } + + return secret, nil +} + // ResetLockValue resets the dictionary-attack value in the TPM; this allows the // TPM to start working again after authentication errors without waiting for // the dictionary-attack defenses to time out. This requires owner @@ -713,6 +823,112 @@ func OwnerReadSRK(rw io.ReadWriter, ownerAuth digest) ([]byte, error) { return tpmutil.Pack(pk) } +// ReadEKCert reads the EKCert from the NVRAM. +// The TCG PC Client specifies additional headers that are to be stored with the EKCert, we parse them +// here and return only the DER encoded certificate. +// TCG PC Client Specific Implementation Specification for Conventional BIOS 7.4.4 +// https://www.trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf +func ReadEKCert(rw io.ReadWriter, ownAuth digest) ([]byte, error) { + const ( + certIndex = 0x1000f000 // TPM_NV_INDEX_EKCert (TPM Main Part 2 TPM Structures 19.1.2) + certTagPCClientStoredCert = 0x1001 // TCG_TAG_PCCLIENT_STORED_CERT + certTagPCClientFullCert = 0x1002 // TCG_TAG_PCCLIENT_FULL_CERT + tcgFullCert = 0 // TCG_FULL_CERT + tcgPartialSmallCert = 1 // TCG_PARTIAL_SMALL_CERT + ) + offset := uint32(0) + var header struct { + Tag uint16 + CertType uint8 + CertSize uint16 + } + + data, err := NVReadValue(rw, certIndex, offset, uint32(binary.Size(header)), ownAuth) + if err != nil { + return nil, err + } + offset = offset + uint32(binary.Size(header)) + buff := bytes.NewReader(data) + + if err := binary.Read(buff, binary.BigEndian, &header); err != nil { + return nil, err + } + + if header.Tag != certTagPCClientStoredCert { + return nil, fmt.Errorf("invalid certificate") + } + + var bufSize uint32 + switch header.CertType { + case tcgFullCert: + var tag uint16 + data, err := NVReadValue(rw, certIndex, offset, uint32(binary.Size(tag)), ownAuth) + if err != nil { + return nil, err + } + bufSize = uint32(header.CertSize) + offset + offset = offset + uint32(binary.Size(tag)) + buff = bytes.NewReader(data) + + if err := binary.Read(buff, binary.BigEndian, &tag); err != nil { + return nil, err + } + + if tag != certTagPCClientFullCert { + return nil, fmt.Errorf("certificate type and tag do not match") + } + case tcgPartialSmallCert: + return nil, fmt.Errorf("certType is not TCG_FULL_CERT: currently do not support partial certs") + default: + return nil, fmt.Errorf("invalid certType: 0x%x", header.CertType) + } + + var ekbuf []byte + for offset < bufSize { + length := bufSize - offset + // TPMs can only read so much memory per command so we read in 128byte chunks. + // 128 was taken from go-tspi. The actual max read seems to be platform dependent + // but cannot be queried on TPM1.2 (and does not seem to appear in any documentation). + if length > 128 { + length = 128 + } + data, err = NVReadValue(rw, certIndex, offset, length, ownAuth) + if err != nil { + return nil, err + } + + ekbuf = append(ekbuf, data...) + offset += length + } + + return ekbuf, nil +} + +// NVReadValue returns the value from a given index, offset, and length in NVRAM. +// See TPM-Main-Part-2-TPM-Structures 19.1. +func NVReadValue(rw io.ReadWriter, index, offset, len uint32, ownAuth digest) ([]byte, error) { + sharedSecretOwn, osaprOwn, err := newOSAPSession(rw, etOwner, khOwner, ownAuth[:]) + if err != nil { + return nil, fmt.Errorf("failed to start new auth session: %v", err) + } + defer osaprOwn.Close(rw) + defer zeroBytes(sharedSecretOwn[:]) + authIn := []interface{}{ordNVReadValue, index, offset, len} + ca, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, sharedSecretOwn[:], authIn) + if err != nil { + return nil, fmt.Errorf("failed to construct owner auth fields: %v", err) + } + data, ra, ret, err := nvReadValue(rw, index, offset, len, ca) + if err != nil { + return nil, fmt.Errorf("failed to read from NVRAM: %v", err) + } + raIn := []interface{}{ret, ordNVReadValue, tpmutil.U32Bytes(data)} + if err := ra.verify(ca.NonceOdd, sharedSecretOwn[:], raIn); err != nil { + return nil, fmt.Errorf("failed to verify authenticity of response: %v", err) + } + return data, nil +} + // OwnerReadPubEK uses owner auth to get a blob representing the public part of the // endorsement key. func OwnerReadPubEK(rw io.ReadWriter, ownerAuth digest) ([]byte, error) { @@ -754,6 +970,11 @@ func ReadPubEK(rw io.ReadWriter) ([]byte, error) { return tpmutil.Pack(pk) } +// GetManufacturer returns the manufacturer ID +func GetManufacturer(rw io.ReadWriter) ([]byte, error) { + return getCapability(rw, capProperty, tpmCapPropManufacturer) +} + // OwnerClear uses owner auth to clear the TPM. After this operation, the TPM // can change ownership. func OwnerClear(rw io.ReadWriter, ownerAuth digest) error { @@ -818,7 +1039,7 @@ func TakeOwnership(rw io.ReadWriter, newOwnerAuth digest, newSRKAuth digest, pub // - Sig must be None // - Key usage must be Storage // - Key must not be migratable - srkRSAParams := rsaKeyParms{ + srkRSAParams := rsaKeyParams{ KeyLength: 2048, NumPrimes: 2, } @@ -826,18 +1047,18 @@ func TakeOwnership(rw io.ReadWriter, newOwnerAuth digest, newSRKAuth digest, pub if err != nil { return err } - srkParams := keyParms{ + srkParams := keyParams{ AlgID: algRSA, EncScheme: esRSAEsOAEPSHA1MGF1, SigScheme: ssNone, - Parms: srkpb, + Params: srkpb, } srk := &key{ - Version: 0x01010000, - KeyUsage: keyStorage, - KeyFlags: 0, - AuthDataUsage: authAlways, - AlgorithmParms: srkParams, + Version: 0x01010000, + KeyUsage: keyStorage, + KeyFlags: 0, + AuthDataUsage: authAlways, + AlgorithmParams: srkParams, } // Get command auth using OIAP with the new owner auth. @@ -899,7 +1120,7 @@ func CreateWrapKey(rw io.ReadWriter, srkAuth []byte, usageAuth digest, migration encMigrationAuth[i] = encAuthDataKey[i] ^ migrationAuth[i] } - rParams := rsaKeyParms{ + rParams := rsaKeyParams{ KeyLength: 2048, NumPrimes: 2, } @@ -925,11 +1146,11 @@ func CreateWrapKey(rw io.ReadWriter, srkAuth []byte, usageAuth digest, migration KeyUsage: keySigning, KeyFlags: 0, AuthDataUsage: authAlways, - AlgorithmParms: keyParms{ + AlgorithmParams: keyParams{ AlgID: algRSA, EncScheme: esNone, SigScheme: ssRSASaPKCS1v15DER, - Parms: rParamsPacked, + Params: rParamsPacked, }, PCRInfo: pcrInfoBytes, } @@ -988,7 +1209,7 @@ func Sign(rw io.ReadWriter, keyAuth []byte, keyHandle tpmutil.Handle, hash crypt defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) - authIn := []interface{}{ordSign, data} + authIn := []interface{}{ordSign, tpmutil.U32Bytes(data)} ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, sharedSecret[:], authIn) if err != nil { return nil, err @@ -999,7 +1220,7 @@ func Sign(rw io.ReadWriter, keyAuth []byte, keyHandle tpmutil.Handle, hash crypt return nil, err } - raIn := []interface{}{ret, ordSign, signature} + raIn := []interface{}{ret, ordSign, tpmutil.U32Bytes(signature)} err = ra.verify(ca.NonceOdd, sharedSecret[:], raIn) if err != nil { return nil, err diff --git a/vendor/github.com/google/go-tpm/tpm/tpm_linux_test.go b/vendor/github.com/google/go-tpm/tpm/tpm_other_test.go similarity index 92% rename from vendor/github.com/google/go-tpm/tpm/tpm_linux_test.go rename to vendor/github.com/google/go-tpm/tpm/tpm_other_test.go index 1f5dffc..921b0c5 100644 --- a/vendor/github.com/google/go-tpm/tpm/tpm_linux_test.go +++ b/vendor/github.com/google/go-tpm/tpm/tpm_other_test.go @@ -1,4 +1,6 @@ -// Copyright (c) 2014, Google Inc. All rights reserved. +// +build !windows + +// Copyright (c) 2014, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/github.com/google/go-tpm/tpm/tpm_test.go b/vendor/github.com/google/go-tpm/tpm/tpm_test.go index 1184af6..fdfb18e 100644 --- a/vendor/github.com/google/go-tpm/tpm/tpm_test.go +++ b/vendor/github.com/google/go-tpm/tpm/tpm_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014, Google Inc. All rights reserved. +// Copyright (c) 2014, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import ( "bytes" "crypto/rand" "crypto/sha1" + "crypto/x509" "io/ioutil" "os" "testing" @@ -57,6 +58,18 @@ func TestGetKeys(t *testing.T) { t.Logf("Got %d keys: % d\n", len(handles), handles) } +func TestGetManufacturer(t *testing.T) { + rwc := openTPMOrSkip(t) + defer rwc.Close() + + vendorID, err := GetManufacturer(rwc) + if err != nil { + t.Fatal("Couldn't read VendorID from TPM:", err) + } + + t.Logf("TPM VendorID: %v\n", vendorID) +} + func TestPcrExtend(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() @@ -84,6 +97,24 @@ func TestPcrExtend(t *testing.T) { } } +func TestReadEKCert(t *testing.T) { + rwc := openTPMOrSkip(t) + defer rwc.Close() + + ownAuth := getAuth(ownerAuthEnvVar) + cert, err := ReadEKCert(rwc, ownAuth) + if err != nil { + t.Fatal("Unable to read EKCert from NVRAM:", err) + } + + x509cert, err := x509.ParseCertificate(cert) + if err != nil { + t.Logf("Malformed certificate: %v\n", err) + } else { + t.Logf("Certificate: %v\n", x509cert) + } +} + func TestReadPCR(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() @@ -185,13 +216,13 @@ func TestResizeableSlice(t *testing.T) { t.Fatal("Couldn't read random bytes into the byte array") } - bb, err := tpmutil.Pack(ra, b) + bb, err := tpmutil.Pack(ra, tpmutil.U32Bytes(b)) if err != nil { t.Fatal("Couldn't pack the bytes:", err) } var ra2 responseAuth - var b2 []byte + var b2 tpmutil.U32Bytes if _, err := tpmutil.Unpack(bb, &ra2, &b2); err != nil { t.Fatal("Couldn't unpack the resizeable values:", err) } @@ -501,12 +532,12 @@ func TestTakeOwnership(t *testing.T) { srkAuth := getAuth(srkAuthEnvVar) // This test assumes that the TPM has been cleared using OwnerClear. - pubek, err := ReadPubEK(rwc) + pubEK, err := ReadPubEK(rwc) if err != nil { t.Fatal("Couldn't read the public endorsement key from the TPM:", err) } - if err := TakeOwnership(rwc, ownerAuth, srkAuth, pubek); err != nil { + if err := TakeOwnership(rwc, ownerAuth, srkAuth, pubEK); err != nil { t.Fatal("Couldn't take ownership of the TPM:", err) } } diff --git a/vendor/github.com/google/go-tpm/tpm/tpm_windows_test.go b/vendor/github.com/google/go-tpm/tpm/tpm_windows_test.go index 1f9ec11..bc452c3 100644 --- a/vendor/github.com/google/go-tpm/tpm/tpm_windows_test.go +++ b/vendor/github.com/google/go-tpm/tpm/tpm_windows_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,13 +17,11 @@ package tpm import ( "io" "testing" - - "github.com/google/go-tpm/tpmutil" ) // Skip the test if we can't open the TPM. func openTPMOrSkip(t *testing.T) io.ReadWriteCloser { - rwc, err := OpenTPM(tpmutil.HighPriority) + rwc, err := OpenTPM() if err != nil { t.Skipf("Skipping test, since we can't access the TPM: %s\n", err) } diff --git a/vendor/github.com/google/go-tpm/tpm/verify.go b/vendor/github.com/google/go-tpm/tpm/verify.go index b8b5e1e..2eccac3 100644 --- a/vendor/github.com/google/go-tpm/tpm/verify.go +++ b/vendor/github.com/google/go-tpm/tpm/verify.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014, Google Inc. All rights reserved. +// Copyright (c) 2014, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -43,14 +43,14 @@ func UnmarshalRSAPublicKey(keyBlob []byte) (*rsa.PublicKey, error) { // unmarshalRSAPublicKey unmarshals a TPM key into a crypto/rsa.PublicKey. func (k *key) unmarshalRSAPublicKey() (*rsa.PublicKey, error) { // Currently, we only support algRSA - if k.AlgorithmParms.AlgID != algRSA { + if k.AlgorithmParams.AlgID != algRSA { return nil, errors.New("only TPM_ALG_RSA is supported") } - // This means that k.AlgorithmsParms.Parms is an rsaKeyParms, which is + // This means that k.AlgorithmsParams.Params is an rsaKeyParams, which is // enough to create the exponent, and k.PubKey contains the key. - var rsakp rsaKeyParms - if _, err := tpmutil.Unpack(k.AlgorithmParms.Parms, &rsakp); err != nil { + var rsakp rsaKeyParams + if _, err := tpmutil.Unpack(k.AlgorithmParams.Params, &rsakp); err != nil { return nil, err } @@ -82,14 +82,14 @@ func UnmarshalPubRSAPublicKey(keyBlob []byte) (*rsa.PublicKey, error) { // This is almost identical to the identically named function for a TPM key. func (pk *pubKey) unmarshalRSAPublicKey() (*rsa.PublicKey, error) { // Currently, we only support algRSA - if pk.AlgorithmParms.AlgID != algRSA { + if pk.AlgorithmParams.AlgID != algRSA { return nil, errors.New("only TPM_ALG_RSA is supported") } - // This means that pk.AlgorithmsParms.Parms is an rsaKeyParms, which is + // This means that pk.AlgorithmsParams.Params is an rsaKeyParams, which is // enough to create the exponent, and pk.Key contains the key. - var rsakp rsaKeyParms - if _, err := tpmutil.Unpack(pk.AlgorithmParms.Parms, &rsakp); err != nil { + var rsakp rsaKeyParams + if _, err := tpmutil.Unpack(pk.AlgorithmParams.Params, &rsakp); err != nil { return nil, err } @@ -105,9 +105,9 @@ func (pk *pubKey) unmarshalRSAPublicKey() (*rsa.PublicKey, error) { return rsapk, nil } -// newQuoteInfo computes a quoteInfo structure for a given pair of data and PCR +// NewQuoteInfo computes a quoteInfo structure for a given pair of data and PCR // values. -func newQuoteInfo(data []byte, pcrNums []int, pcrs []byte) (*quoteInfo, error) { +func NewQuoteInfo(data []byte, pcrNums []int, pcrs []byte) ([]byte, error) { // Compute the composite hash for these PCRs. pcrSel, err := newPCRSelection(pcrNums) if err != nil { @@ -126,17 +126,12 @@ func newQuoteInfo(data []byte, pcrNums []int, pcrs []byte) (*quoteInfo, error) { } copy(qi.CompositeDigest[:], comp) - return qi, nil + return tpmutil.Pack(qi) } // VerifyQuote verifies a quote against a given set of PCRs. func VerifyQuote(pk *rsa.PublicKey, data []byte, quote []byte, pcrNums []int, pcrs []byte) error { - qi, err := newQuoteInfo(data, pcrNums, pcrs) - if err != nil { - return err - } - - p, err := tpmutil.Pack(qi) + p, err := NewQuoteInfo(data, pcrNums, pcrs) if err != nil { return err } diff --git a/vendor/github.com/google/go-tpm/tpm2/README.md b/vendor/github.com/google/go-tpm/tpm2/README.md index 3951183..4d0ff8b 100644 --- a/vendor/github.com/google/go-tpm/tpm2/README.md +++ b/vendor/github.com/google/go-tpm/tpm2/README.md @@ -1,19 +1,35 @@ # TPM 2.0 client library -## Integration tests +## Tests -`tpm2_test.go` contains integration tests that run against a real TPM device -(emulated or hardware). +This library contains unit tests in `github.com/google/go-tpm/tpm2`, which just +tests that various encoding and error checking functions work correctly. It also +contains more comprehensive integration tests in +`github.com/google/go-tpm/tpm2/test`, which run actual commands on a TPM. -By default, running `go test` will skip them. To run the tests on a host with -available TPM device, run - -``` -go test --tpm_path=/dev/tpm0 +By default, these integration tests are run against the +[`go-tpm-tools`](https://github.com/google/go-tpm-tools) +simulator, which is baesed on the +[Microsoft Reference TPM2 code](https://github.com/microsoft/ms-tpm-20-ref). To +run both the unit and integration tests, run (in this directory) +```bash +go test . ./test ``` -where `/dev/tpm0` is path to a TPM character device or a Unix socket. +These integration tests can also be run against a real TPM device. This is +slightly more complex as the tests often need to be built as a normal user and +then executed as root. For example, +```bash +# Build the test binary without running it +go test -c github.com/google/go-tpm/tpm2/test +# Execute the test binary as root +sudo ./test.test --tpm-path=/dev/tpmrm0 +``` +On Linux, The `--tpm-path` causes the integration tests to be run against a +real TPM located at that path (usually `/dev/tpmrm0` or `/dev/tpm0`). On Windows, the story is similar, execept that +the `--use-tbs` flag is used instead. -Tip: if your TPM host is remote and you don't want to install Go on it, use `go -test -c` to compile a test binary. It can be copied to remote host and run -without extra dependencies. +Tip: if your TPM host is remote and you don't want to install Go on it, this +same two-step process can be used. The test binary can be copied to a remote +host and run without extra installation (as the test binary has very few +*runtime* dependancies). diff --git a/vendor/github.com/google/go-tpm/tpm2/constants.go b/vendor/github.com/google/go-tpm/tpm2/constants.go index dd39327..d9599d8 100644 --- a/vendor/github.com/google/go-tpm/tpm2/constants.go +++ b/vendor/github.com/google/go-tpm/tpm2/constants.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,14 +19,16 @@ import ( "crypto/sha1" "crypto/sha256" "crypto/sha512" + "fmt" "hash" "github.com/google/go-tpm/tpmutil" ) -func init() { - tpmutil.UseTPM20LengthPrefixSize() -} +// MAX_DIGEST_BUFFER is the maximum size of []byte request or response fields. +// Typically used for chunking of big blobs of data (such as for hashing or +// encryption). +const maxDigestBuffer = 1024 // Algorithm represents a TPM_ALG_ID value. type Algorithm uint16 @@ -41,13 +43,31 @@ func (a Algorithm) UsesCount() bool { return a == AlgECDAA } +// UsesHash returns true if the algorithm requires the use of a hash. +func (a Algorithm) UsesHash() bool { + return a == AlgOAEP +} + +// HashConstructor returns a function that can be used to make a +// hash.Hash using the specified algorithm. An error is returned +// if the algorithm is not a hash algorithm. +func (a Algorithm) HashConstructor() (func() hash.Hash, error) { + c, ok := hashConstructors[a] + if !ok { + return nil, fmt.Errorf("algorithm not supported: 0x%x", a) + } + return c, nil +} + // Supported Algorithms. const ( AlgUnknown Algorithm = 0x0000 AlgRSA Algorithm = 0x0001 AlgSHA1 Algorithm = 0x0004 + AlgHMAC Algorithm = 0x0005 AlgAES Algorithm = 0x0006 AlgKeyedHash Algorithm = 0x0008 + AlgXOR Algorithm = 0x000A AlgSHA256 Algorithm = 0x000B AlgSHA384 Algorithm = 0x000C AlgSHA512 Algorithm = 0x000D @@ -61,6 +81,7 @@ const ( AlgECDAA Algorithm = 0x001A AlgKDF2 Algorithm = 0x0021 AlgECC Algorithm = 0x0023 + AlgSymCipher Algorithm = 0x0025 AlgCTR Algorithm = 0x0040 AlgOFB Algorithm = 0x0041 AlgCBC Algorithm = 0x0042 @@ -68,6 +89,22 @@ const ( AlgECB Algorithm = 0x0044 ) +// HandleType defines a type of handle. +type HandleType uint8 + +// Supported handle types +const ( + HandleTypePCR HandleType = 0x00 + HandleTypeNVIndex HandleType = 0x01 + HandleTypeHMACSession HandleType = 0x02 + HandleTypeLoadedSession HandleType = 0x02 + HandleTypePolicySession HandleType = 0x03 + HandleTypeSavedSession HandleType = 0x03 + HandleTypePermanent HandleType = 0x40 + HandleTypeTransient HandleType = 0x80 + HandleTypePersistent HandleType = 0x81 +) + // SessionType defines the type of session created in StartAuthSession. type SessionType uint8 @@ -78,6 +115,24 @@ const ( SessionTrial SessionType = 0x03 ) +// SessionAttributes represents an attribute of a session. +type SessionAttributes byte + +// Session Attributes (Structures 8.4 TPMA_SESSION) +const ( + AttrContinueSession SessionAttributes = 1 << iota + AttrAuditExclusive + AttrAuditReset + _ // bit 3 reserved + _ // bit 4 reserved + AttrDecrypt + AttrEcrypt + AttrAudit +) + +// EmptyAuth represents the empty authorization value. +var EmptyAuth []byte + // KeyProp is a bitmask used in Attributes field of key templates. Individual // flags should be OR-ed to form a full mask. type KeyProp uint32 @@ -89,6 +144,7 @@ const ( FlagSensitiveDataOrigin KeyProp = 0x00000020 FlagUserWithAuth KeyProp = 0x00000040 FlagAdminWithPolicy KeyProp = 0x00000080 + FlagNoDA KeyProp = 0x00000400 FlagRestricted KeyProp = 0x00010000 FlagDecrypt KeyProp = 0x00020000 FlagSign KeyProp = 0x00040000 @@ -100,6 +156,75 @@ const ( FlagFixedParent | FlagSensitiveDataOrigin | FlagUserWithAuth ) +// TPMProp represents the index of a TPM property in a call to GetCapability(). +type TPMProp uint32 + +// TPM Capability Properties, see TPM 2.0 Spec, Rev 1.38, Table 23 +const ( + // Fixed TPM Properties (PT_FIXED) + FamilyIndicator TPMProp = 0x100 + iota + SpecLevel + SpecRevision + SpecDayOfYear + SpecYear + Manufacturer + VendorString1 + VendorString2 + VendorString3 + VendorString4 + VendorTPMType + FirmwareVersion1 + FirmwareVersion2 + InputMaxBufferSize + TransientObjectsMin + PersistentObjectsMin + LoadedObjectsMin + ActiveSessionsMax + PCRCount + PCRSelectMin + ContextGapMax + _ // (PT_FIXED + 21) is skipped + NVCountersMax + NVIndexMax + MemoryMethod + ClockUpdate + ContextHash + ContextSym + ContextSymSize + OrderlyCount + CommandMaxSize + ResponseMaxSize + DigestMaxSize + ObjectContextMaxSize + SessionContextMaxSize + PSFamilyIndicator + PSSpecLevel + PSSpecRevision + PSSpecDayOfYear + PSSpecYear + SplitSigningMax + TotalCommands + LibraryCommands + VendorCommands + NVMaxBufferSize + TPMModes + CapabilityMaxBufferSize + + PCRFirst TPMProp = 0x00000000 + HMACSessionFirst TPMProp = 0x02000000 + LoadedSessionFirst TPMProp = 0x02000000 + PolicySessionFirst TPMProp = 0x03000000 + ActiveSessionFirst TPMProp = 0x03000000 + TransientFirst TPMProp = 0x80000000 + PersistentFirst TPMProp = 0x81000000 + PersistentLast TPMProp = 0x81FFFFFF + PlatformPersistent TPMProp = 0x81800000 + NVIndexFirst TPMProp = 0x01000000 + NVIndexLast TPMProp = 0x01FFFFFF + PermanentFirst TPMProp = 0x40000000 + PermanentLast TPMProp = 0x4000010F +) + // Reserved Handles. const ( HandleOwner tpmutil.Handle = 0x40000001 + iota @@ -119,7 +244,7 @@ const ( // Capability identifies some TPM property or state type. type Capability uint32 -// TPM Capabilies. +// TPM Capabilities. const ( CapabilityAlgs Capability = iota CapabilityHandles @@ -133,12 +258,16 @@ const ( CapabilityAuthPolicies ) +// TPM Structure Tags. Tags are used to disambiguate structures, similar to Alg +// values: tag value defines what kind of data lives in a nested field. const ( - tagNull tpmutil.Tag = 0x8000 - tagNoSessions tpmutil.Tag = 0x8001 - tagSessions tpmutil.Tag = 0x8002 - tagAttestCertify tpmutil.Tag = 0x8017 - tagHashCheck tpmutil.Tag = 0x8024 + TagNull tpmutil.Tag = 0x8000 + TagNoSessions tpmutil.Tag = 0x8001 + TagSessions tpmutil.Tag = 0x8002 + TagAttestCertify tpmutil.Tag = 0x8017 + TagAttestQuote tpmutil.Tag = 0x8018 + TagAttestCreation tpmutil.Tag = 0x801a + TagHashCheck tpmutil.Tag = 0x8024 ) // StartupType instructs the TPM on how to handle its state during Shutdown or @@ -179,41 +308,48 @@ var toGoCurve = map[EllipticCurve]elliptic.Curve{ const ( cmdEvictControl tpmutil.Command = 0x00000120 cmdUndefineSpace tpmutil.Command = 0x00000122 - cmdClockSet tpmutil.Command = 0x00000128 cmdDefineSpace tpmutil.Command = 0x0000012A - cmdPCRAllocate tpmutil.Command = 0x0000012B cmdCreatePrimary tpmutil.Command = 0x00000131 cmdIncrementNVCounter tpmutil.Command = 0x00000134 cmdWriteNV tpmutil.Command = 0x00000137 cmdPCREvent tpmutil.Command = 0x0000013C cmdStartup tpmutil.Command = 0x00000144 cmdShutdown tpmutil.Command = 0x00000145 - cmdStirRandom tpmutil.Command = 0x00000146 cmdActivateCredential tpmutil.Command = 0x00000147 cmdCertify tpmutil.Command = 0x00000148 + cmdCertifyCreation tpmutil.Command = 0x0000014A cmdReadNV tpmutil.Command = 0x0000014E - cmdCreate tpmutil.Command = 0x00000153 - cmdLoad tpmutil.Command = 0x00000157 - cmdQuote tpmutil.Command = 0x00000158 - cmdSign tpmutil.Command = 0x0000015D - cmdUnseal tpmutil.Command = 0x0000015E - cmdContextLoad tpmutil.Command = 0x00000161 - cmdContextSave tpmutil.Command = 0x00000162 - cmdFlushContext tpmutil.Command = 0x00000165 - cmdLoadExternal tpmutil.Command = 0x00000167 - cmdMakeCredential tpmutil.Command = 0x00000168 - cmdReadPublicNV tpmutil.Command = 0x00000169 - cmdReadPublic tpmutil.Command = 0x00000173 - cmdStartAuthSession tpmutil.Command = 0x00000176 - cmdGetCapability tpmutil.Command = 0x0000017A - cmdGetRandom tpmutil.Command = 0x0000017B - cmdHash tpmutil.Command = 0x0000017D - cmdPCRRead tpmutil.Command = 0x0000017E - cmdPolicyPCR tpmutil.Command = 0x0000017F - cmdReadClock tpmutil.Command = 0x00000181 - cmdPCRExtend tpmutil.Command = 0x00000182 - cmdPolicyGetDigest tpmutil.Command = 0x00000189 - cmdPolicyPassword tpmutil.Command = 0x0000018C + // CmdPolicySecret is a command code for TPM2_PolicySecret. + // It's exported for computing of default AuthPolicy value. + CmdPolicySecret tpmutil.Command = 0x00000151 + cmdCreate tpmutil.Command = 0x00000153 + cmdLoad tpmutil.Command = 0x00000157 + cmdQuote tpmutil.Command = 0x00000158 + cmdRSADecrypt tpmutil.Command = 0x00000159 + cmdSign tpmutil.Command = 0x0000015D + cmdUnseal tpmutil.Command = 0x0000015E + cmdContextLoad tpmutil.Command = 0x00000161 + cmdContextSave tpmutil.Command = 0x00000162 + cmdEncryptDecrypt tpmutil.Command = 0x00000164 + cmdFlushContext tpmutil.Command = 0x00000165 + cmdLoadExternal tpmutil.Command = 0x00000167 + cmdMakeCredential tpmutil.Command = 0x00000168 + cmdReadPublicNV tpmutil.Command = 0x00000169 + cmdReadPublic tpmutil.Command = 0x00000173 + cmdRSAEncrypt tpmutil.Command = 0x00000174 + cmdStartAuthSession tpmutil.Command = 0x00000176 + cmdGetCapability tpmutil.Command = 0x0000017A + cmdGetRandom tpmutil.Command = 0x0000017B + cmdHash tpmutil.Command = 0x0000017D + cmdPCRRead tpmutil.Command = 0x0000017E + // CmdPolicyPCR is the command code for TPM2_PolicyPCR. + // It's exported for computing AuthPolicy values for PCR-based sessions. + CmdPolicyPCR tpmutil.Command = 0x0000017F + cmdReadClock tpmutil.Command = 0x00000181 + cmdPCRExtend tpmutil.Command = 0x00000182 + cmdPolicyGetDigest tpmutil.Command = 0x00000189 + cmdPolicyPassword tpmutil.Command = 0x0000018C + cmdEncryptDecrypt2 tpmutil.Command = 0x00000193 ) // Regular TPM 2.0 devices use 24-bit mask (3 bytes) for PCR selection. @@ -242,3 +378,32 @@ var hashConstructors = map[Algorithm]func() hash.Hash{ AlgSHA384: sha512.New384, AlgSHA512: sha512.New, } + +// NVAttr is a bitmask used in Attributes field of NV indexes. Individual +// flags should be OR-ed to form a full mask. +type NVAttr uint32 + +// NV Attributes +const ( + AttrPPWrite NVAttr = 0x00000001 + AttrOwnerWrite NVAttr = 0x00000002 + AttrAuthWrite NVAttr = 0x00000004 + AttrPolicyWrite NVAttr = 0x00000008 + AttrPolicyDelete NVAttr = 0x00000400 + AttrWriteLocked NVAttr = 0x00000800 + AttrWriteAll NVAttr = 0x00001000 + AttrWriteDefine NVAttr = 0x00002000 + AttrWriteSTClear NVAttr = 0x00004000 + AttrGlobalLock NVAttr = 0x00008000 + AttrPPRead NVAttr = 0x00010000 + AttrOwnerRead NVAttr = 0x00020000 + AttrAuthRead NVAttr = 0x00040000 + AttrPolicyRead NVAttr = 0x00080000 + AttrNoDA NVAttr = 0x02000000 + AttrOrderly NVAttr = 0x04000000 + AttrClearSTClear NVAttr = 0x08000000 + AttrReadLocked NVAttr = 0x10000000 + AttrWritten NVAttr = 0x20000000 + AttrPlatformCreate NVAttr = 0x40000000 + AttrReadSTClear NVAttr = 0x80000000 +) diff --git a/vendor/github.com/google/go-tpm/tpm2/credactivation/credential_activation.go b/vendor/github.com/google/go-tpm/tpm2/credactivation/credential_activation.go new file mode 100644 index 0000000..a8a1786 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/credactivation/credential_activation.go @@ -0,0 +1,139 @@ +// Copyright (c) 2018, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package credactivation implements generation of data blobs to be used +// when invoking the ActivateCredential command, on a TPM. +package credactivation + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/rand" + "crypto/rsa" + "errors" + "fmt" + "io" + + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpmutil" +) + +// Labels for use in key derivation or OAEP encryption. +const ( + labelIdentity = "IDENTITY" + labelStorage = "STORAGE" + labelIntegrity = "INTEGRITY" +) + +// Generate returns a TPM2B_ID_OBJECT & TPM2B_ENCRYPTED_SECRET for use in +// credential activation. +// This has been tested on EKs compliant with TCG 2.0 EK Credential Profile +// specification, revision 14. +// The pub parameter must be a pointer to rsa.PublicKey. +// The secret parameter must not be longer than the longest digest size implemented +// by the TPM. A 32 byte secret is a safe, recommended default. +// +// This function implements Credential Protection as defined in section 24 of the TPM +// specification revision 2 part 1, with the additional caveat of not supporting ECC EKs. +// See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ +func Generate(aik *tpm2.HashValue, pub crypto.PublicKey, symBlockSize int, secret []byte) ([]byte, []byte, error) { + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + return nil, nil, errors.New("only RSA public keys are supported for credential activation") + } + + return generateRSA(aik, rsaPub, symBlockSize, secret, rand.Reader) +} + +func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secret []byte, rnd io.Reader) ([]byte, []byte, error) { + hashNew, err := aik.Alg.HashConstructor() + if err != nil { + return nil, nil, err + } + + // The seed length should match the keysize used by the EKs symmetric cipher. + // For typical RSA EKs, this will be 128 bits (16 bytes). + // Spec: TCG 2.0 EK Credential Profile revision 14, section 2.1.5.1. + seed := make([]byte, symBlockSize) + if _, err := io.ReadFull(rnd, seed); err != nil { + return nil, nil, fmt.Errorf("generating seed: %v", err) + } + + // Encrypt the seed value using the provided public key. + // See annex B, section 10.4 of the TPM specification revision 2 part 1. + label := append([]byte(labelIdentity), 0) + encSecret, err := rsa.EncryptOAEP(hashNew(), rnd, pub, seed, label) + if err != nil { + return nil, nil, fmt.Errorf("generating encrypted seed: %v", err) + } + + // Generate the encrypted credential by convolving the seed with the digest of + // the AIK, and using the result as the key to encrypt the secret. + // See section 24.4 of TPM 2.0 specification, part 1. + aikNameEncoded, err := aik.Encode() + if err != nil { + return nil, nil, fmt.Errorf("encoding aikName: %v", err) + } + symmetricKey, err := tpm2.KDFa(aik.Alg, seed, labelStorage, aikNameEncoded, nil, len(seed)*8) + if err != nil { + return nil, nil, fmt.Errorf("generating symmetric key: %v", err) + } + c, err := aes.NewCipher(symmetricKey) + if err != nil { + return nil, nil, fmt.Errorf("symmetric cipher setup: %v", err) + } + cv, err := tpmutil.Pack(tpmutil.U16Bytes(secret)) + if err != nil { + return nil, nil, fmt.Errorf("generating cv (TPM2B_Digest): %v", err) + } + + // IV is all null bytes. encIdentity represents the encrypted credential. + encIdentity := make([]byte, len(cv)) + cipher.NewCFBEncrypter(c, make([]byte, len(symmetricKey))).XORKeyStream(encIdentity, cv) + + // Generate the integrity HMAC, which is used to protect the integrity of the + // encrypted structure. + // See section 24.5 of the TPM specification revision 2 part 1. + macKey, err := tpm2.KDFa(aik.Alg, seed, labelIntegrity, nil, nil, hashNew().Size()*8) + if err != nil { + return nil, nil, fmt.Errorf("generating HMAC key: %v", err) + } + + mac := hmac.New(hashNew, macKey) + mac.Write(encIdentity) + mac.Write(aikNameEncoded) + integrityHMAC := mac.Sum(nil) + + idObject := &tpm2.IDObject{ + IntegrityHMAC: integrityHMAC, + EncIdentity: encIdentity, + } + id, err := tpmutil.Pack(idObject) + if err != nil { + return nil, nil, fmt.Errorf("encoding IDObject: %v", err) + } + + packedID, err := tpmutil.Pack(tpmutil.U16Bytes(id)) + if err != nil { + return nil, nil, fmt.Errorf("packing id: %v", err) + } + packedEncSecret, err := tpmutil.Pack(tpmutil.U16Bytes(encSecret)) + if err != nil { + return nil, nil, fmt.Errorf("packing encSecret: %v", err) + } + + return packedID, packedEncSecret, nil +} diff --git a/vendor/github.com/google/go-tpm/tpm2/credactivation/credential_activation_test.go b/vendor/github.com/google/go-tpm/tpm2/credactivation/credential_activation_test.go new file mode 100644 index 0000000..1fd21ae --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/credactivation/credential_activation_test.go @@ -0,0 +1,67 @@ +// Copyright (c) 2018, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package credactivation + +import ( + "bytes" + "crypto/rsa" + "encoding/base64" + "math/big" + insecureRand "math/rand" + "testing" + + "github.com/google/go-tpm/tpm2" +) + +func mustDecodeBase64(in string, t *testing.T) []byte { + d, err := base64.StdEncoding.DecodeString(in) + if err != nil { + t.Fatal(err) + } + return d +} + +func TestCredentialActivation(t *testing.T) { + // These values were independently tested/derived-from from TCG 2.0.38-compliant hardware. + n, ok := new(big.Int).SetString("21781359931719875035142348126986833104406251147281912291128410183893060751686286557235105177011038982931176491091366273712008774268043339103634631078508025847736699362996617038459342869130285665581223736549299195932345592253444537445668838861984376176364138265105552997914795970576284975601851753797509031880704132484924873723738272046545068767315124876824011679223652746414206246649323781826144832659865886735865286033208505363212876011411861316385696414905053502571926429826843117374014575605550176234010475825493066764152314323863950174296024693364113127191375694561947145403061250952175062770094723660429657392597", 10) + if !ok { + t.Fatalf("Failed to parse publicN string.") + } + public := rsa.PublicKey{ + N: n, + E: 65537, + } + + aikDigest := mustDecodeBase64("5snpf9qRfKD2Tb72eLAZqC/a/MyUhg+IvdwDZkTJK9w=", t) + expected := mustDecodeBase64("AEQAIIQNQu1RkQagbyN+7JlCKUfwBJxIsONZ2/4BD7Q4A15+BcDylTlcvTDgl1CdTuiZk3JcechnrpbfdDXynZ9Sp0uOAwEApDH7zhzLAqsNMSiEdv0xoGrGf/sOCYzSccZ1pDIv7uHON3yMMrX8beOLtCZ9vEQ3vW4i6NdWUJEd/UeMYuc1+Ucu4IB5teUtExhNyvtOXEM7FNXnKooS2ltLA0L7jlkyqwGM7CE0MK4jeFvy13RFNek6S5Rd5MH3RpBuqpL5NjX/yr4g7xCyE2RmXrCSD2DiTm6wU/PtOxYXUVdXeuLaLD69g5pnEAWhARuYa9SomBI8Ewvcxm+slfJpTK/Unrg+FN/d/n0k0IajklNli/jRhuQh5nhrTZXg80kPsEGraSP8eJof49vR643EtoO88jzpTC+/9Tu3yiGCCxEMqR2szA==", t) + secret := mustDecodeBase64("AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg=", t) + + aikName := &tpm2.HashValue{ + Alg: tpm2.AlgSHA256, + Value: aikDigest, + } + + idObject, wrappedCredential, err := generateRSA(aikName, &public, 16, secret, insecureRand.New(insecureRand.NewSource(99))) + if err != nil { + t.Fatal(err) + } + activationBlob := append(idObject, wrappedCredential...) + + if !bytes.Equal(expected, activationBlob) { + t.Errorf("generate(%v, %v, %v) returned incorrect result", aikName, public, secret) + t.Logf(" Got: %v", activationBlob) + t.Logf(" Want: %v", expected) + } +} diff --git a/vendor/github.com/google/go-tpm/tpm2/encoding_test.go b/vendor/github.com/google/go-tpm/tpm2/encoding_test.go index ee70c32..84d3e04 100644 --- a/vendor/github.com/google/go-tpm/tpm2/encoding_test.go +++ b/vendor/github.com/google/go-tpm/tpm2/encoding_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,17 +16,57 @@ package tpm2 import ( "bytes" + "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "encoding/hex" - "math/big" "reflect" "testing" "github.com/google/go-tpm/tpmutil" ) +var ( + pcrSelection0 = PCRSelection{Hash: AlgSHA1, PCRs: []int{0}} + pcrSelection1 = PCRSelection{Hash: AlgSHA1, PCRs: []int{1}} + pcrSelection7 = PCRSelection{Hash: AlgSHA1, PCRs: []int{7}} + defaultPassword = "\x01\x02\x03\x04" +) + +func TestEncodeDecodeCreationData(t *testing.T) { + parentQualified := tpmutil.Handle(101) + cd := CreationData{ + PCRSelection: PCRSelection{Hash: AlgSHA1, PCRs: []int{7}}, + PCRDigest: []byte{1, 2, 3}, + Locality: 32, + ParentNameAlg: AlgSHA1, + ParentName: Name{ + Digest: &HashValue{ + Alg: AlgSHA1, + Value: make([]byte, crypto.SHA1.Size()), + }, + }, + ParentQualifiedName: Name{ + Handle: &parentQualified, + }, + OutsideInfo: []byte{7, 8, 9}, + } + + encoded, err := cd.encode() + if err != nil { + t.Fatalf("error encoding CreationData: %v", err) + } + decoded, err := DecodeCreationData(encoded) + if err != nil { + t.Fatalf("error decoding CreationData: %v", err) + } + + if !reflect.DeepEqual(*decoded, cd) { + t.Errorf("got decoded value:\n%v\nwant:\n%v", decoded, cd) + } +} + func TestDecodeReadPCRs(t *testing.T) { testRespBytes, err := hex.DecodeString("800100000032000000000000001400000001000403800000000000010014427d27fe15f8f69736e02b6007b8f6ea674c0745") if err != nil { @@ -37,8 +77,24 @@ func TestDecodeReadPCRs(t *testing.T) { } } -func TestEncodeDecodeTPMLSelection(t *testing.T) { - buf, err := encodeTPMLPCRSelection(pcrSelection) +func TestSingleEncodeDecodeTPMLSelection(t *testing.T) { + buf, err := encodeTPMLPCRSelection(pcrSelection7) + if err != nil { + t.Fatal(err) + } + got, err := decodeOneTPMLPCRSelection(bytes.NewBuffer(buf)) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(got, pcrSelection7) { + t.Errorf("after decoding: %+v, before encoding: %+v", got, pcrSelection7) + } +} + +func TestMultiArgsEncodeDecodeTPMLSelection(t *testing.T) { + multipcrselection := []PCRSelection{pcrSelection0, pcrSelection1, pcrSelection7} + buf, err := encodeTPMLPCRSelection(multipcrselection...) if err != nil { t.Fatal(err) } @@ -47,8 +103,8 @@ func TestEncodeDecodeTPMLSelection(t *testing.T) { t.Fatal(err) } - if !reflect.DeepEqual(got, pcrSelection) { - t.Errorf("after decoding: %+v, before encoding: %+v", got, pcrSelection) + if !reflect.DeepEqual(got, multipcrselection) { + t.Errorf("after decoding: %+v, before encoding %+v", got, multipcrselection) } } @@ -69,12 +125,12 @@ func TestDecodeGetCapability(t *testing.T) { t.Fatal(err) } - capReported, handles, err := decodeGetCapability(testRespBytes[10:]) + handles, moreData, err := decodeGetCapability(testRespBytes[10:]) if err != nil { t.Fatal(err) } - if capReported != CapabilityHandles || len(handles) != 0 { - t.Fatalf("got: (%v, %v), want: (%v, %v)", capReported, handles, CapabilityHandles, 0) + if len(handles) != 0 || moreData { + t.Fatalf("got: (%v, %v), want: (%v, %v)", handles, moreData, 0, false) } } @@ -90,7 +146,8 @@ func TestEncodeLoad(t *testing.T) { } privateBlob := testCmdBytes[33:123] publicBlob := testCmdBytes[125:] - cmdBytes, err := encodeLoad(tpmutil.Handle(0x80000000), defaultPassword, publicBlob, privateBlob) + auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(defaultPassword)} + cmdBytes, err := encodeLoad(tpmutil.Handle(0x80000000), auth, publicBlob, privateBlob) if err != nil { t.Fatalf("encodeLoad failed %s", err) } @@ -111,26 +168,26 @@ func TestDecodeLoad(t *testing.T) { } func TestEncodeCreate(t *testing.T) { - testCmdBytes, err := hex.DecodeString("80020000004d00000131400000010000000940000009000001000000080004010203040000001a0001000400030072000000060080004300100400000100010000000000000001000403800000") + testCmdBytes, err := hex.DecodeString("80020000004d00000131400000010000000940000009000001000000090004010203040001FF001a0001000400030072000000060080004300100400000100010000000000000001000403800000") if err != nil { t.Fatal(err) } params := Public{ Type: AlgRSA, NameAlg: AlgSHA1, - Attributes: 0x00030072, + Attributes: FlagStorageDefault, RSAParameters: &RSAParams{ Symmetric: &SymScheme{ Alg: AlgAES, KeyBits: 128, Mode: AlgCFB, }, - KeyBits: 1024, - Exponent: uint32(0x00010001), - Modulus: big.NewInt(0), + KeyBits: 1024, + ExponentRaw: defaultRSAExponent, }, } - cmdBytes, err := encodeCreate(HandleOwner, pcrSelection, "", defaultPassword, params) + auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession} + cmdBytes, err := encodeCreate(HandleOwner, pcrSelection7, auth, defaultPassword, []byte{255} /*sensitiveData*/, params) if err != nil { t.Fatal(err) } @@ -154,7 +211,7 @@ func TestDecodeCreatePrimary(t *testing.T) { t.Fatal(err) } - if _, _, err = decodeCreatePrimary(testRespBytes[10:]); err != nil { + if _, _, _, _, _, _, err = decodeCreatePrimary(testRespBytes[10:]); err != nil { t.Fatal(err) } } @@ -164,7 +221,7 @@ func TestEncodePolicyPCR(t *testing.T) { if err != nil { t.Fatal(err) } - cmdBytes, err := encodePolicyPCR(tpmutil.Handle(0x03000000), []byte(nil), pcrSelection) + cmdBytes, err := encodePolicyPCR(tpmutil.Handle(0x03000000), []byte(nil), pcrSelection7) if err != nil { t.Fatal(err) } @@ -184,7 +241,7 @@ func TestDecodeStartAuthSession(t *testing.T) { } } -func TestDecodeCreateKey(t *testing.T) { +func TestDecodeCreate(t *testing.T) { testRespBytes, err := hex.DecodeString("8002000001ba00000000000001a70076001405f2c6b6035d4" + "fab43fdc2ed0b6544de59ebd07100100e88a20eb9f58f0f13474a8ab6135144f7c" + "49b80f0f1c2f4900458e2c573c94e7d81e413a06031c634890ccf47e6d02762366" + @@ -203,7 +260,7 @@ func TestDecodeCreateKey(t *testing.T) { t.Fatal(err) } - if _, _, err = decodeCreateKey(testRespBytes[10:]); err != nil { + if _, _, _, _, _, err = decodeCreate(testRespBytes[10:]); err != nil { t.Fatal(err) } } @@ -213,7 +270,7 @@ func TestEncodeUnseal(t *testing.T) { if err != nil { t.Fatal(err) } - cmdBytes, err := encodeUnseal(tpmutil.Handle(0x80000001), defaultPassword) + cmdBytes, err := encodeUnseal(HandlePasswordSession, tpmutil.Handle(0x80000001), defaultPassword) if err != nil { t.Fatal(err) } @@ -239,7 +296,7 @@ func TestEncodeQuote(t *testing.T) { t.Fatal(err) } toQuote := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10} - cmdBytes, err := encodeQuote(tpmutil.Handle(0x80000001), defaultPassword, "", toQuote, pcrSelection, 0x0010) + cmdBytes, err := encodeQuote(tpmutil.Handle(0x80000001), defaultPassword, "", toQuote, pcrSelection7, 0x0010) if err != nil { t.Fatal(err) } @@ -302,8 +359,8 @@ func TestEncodeEvictControl(t *testing.T) { } } -func TestEncodePasswordAuthArea(t *testing.T) { - pwAuth, err := encodePasswordAuthArea(defaultPassword) +func TestEncodeAuthArea(t *testing.T) { + pwAuth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(defaultPassword)}) if err != nil { t.Fatal(err) } @@ -312,7 +369,7 @@ func TestEncodePasswordAuthArea(t *testing.T) { t.Fatalf("got: %v, want: %v", pwAuth, want) } - pwAuth, err = encodePasswordAuthArea("") + pwAuth, err = encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte("")}) if err != nil { t.Fatal(err) } @@ -334,7 +391,7 @@ func TestEncodeSensitiveArea(t *testing.T) { } func TestEncodeTPMLPCRSelection(t *testing.T) { - s, err := encodeTPMLPCRSelection(pcrSelection) + s, err := encodeTPMLPCRSelection(pcrSelection7) if err != nil { t.Fatal(err) } @@ -355,7 +412,7 @@ func TestECCParamsEncodeDecode(t *testing.T) { Hash: AlgSHA1, }, CurveID: CurveNISTP256, - Point: ECPoint{X: pk.PublicKey.X, Y: pk.PublicKey.Y}, + Point: ECPoint{XRaw: pk.PublicKey.X.Bytes(), YRaw: pk.PublicKey.Y.Bytes()}, } buf, err := params.encode() @@ -367,6 +424,19 @@ func TestECCParamsEncodeDecode(t *testing.T) { t.Fatalf("decodeECCParams: %v", err) } + if params.Point.X().Cmp(pk.PublicKey.X) != 0 { + t.Fatalf("the deserialized X(big.Int) from ECCParams didn't match the X(Int) generated by ecdsa. got: %+v\nwant: %+v", params.Point.X(), pk.PublicKey.X) + } + if params.Point.Y().Cmp(pk.PublicKey.Y) != 0 { + t.Fatalf("the deserialized Y(big.Int) from ECCParams didn't match the Y(Int) generated by ecdsa. got: %+v\nwant: %+v", params.Point.Y(), pk.PublicKey.Y) + } + if params.Point.X().Cmp(got.Point.X()) != 0 { + t.Fatalf("the deserialized X(big.Int) from ECCParams after encode/decode didn't match the X(big.Int) before. got: %+v\nwant: %+v", got.Point.X(), params.Point.X()) + } + if params.Point.Y().Cmp(got.Point.Y()) != 0 { + t.Fatalf("the deserialized Y(big.Int) from ECCParams after encode/decode didn't match the Y(big.Int) before. got: %+v\nwant: %+v", got.Point.Y(), params.Point.Y()) + } + if !reflect.DeepEqual(got, params) { t.Fatalf("got: %+v\nwant: %+v", got, params) } diff --git a/vendor/github.com/google/go-tpm/tpm2/error.go b/vendor/github.com/google/go-tpm/tpm2/error.go new file mode 100644 index 0000000..2bdcf3a --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/error.go @@ -0,0 +1,362 @@ +package tpm2 + +import ( + "fmt" + + "github.com/google/go-tpm/tpmutil" +) + +type ( + // RCFmt0 holds Format 0 error codes + RCFmt0 uint8 + + // RCFmt1 holds Format 1 error codes + RCFmt1 uint8 + + // RCWarn holds error codes used in warnings + RCWarn uint8 + + // RCIndex is used to reference arguments, handles and sessions in errors + RCIndex uint8 +) + +// Format 0 error codes. +const ( + RCInitialize RCFmt0 = 0x00 + RCFailure = 0x01 + RCSequence = 0x03 + RCPrivate = 0x0B + RCHMAC = 0x19 + RCDisabled = 0x20 + RCExclusive = 0x21 + RCAuthType = 0x24 + RCAuthMissing = 0x25 + RCPolicy = 0x26 + RCPCR = 0x27 + RCPCRChanged = 0x28 + RCUpgrade = 0x2D + RCTooManyContexts = 0x2E + RCAuthUnavailable = 0x2F + RCReboot = 0x30 + RCUnbalanced = 0x31 + RCCommandSize = 0x42 + RCCommandCode = 0x43 + RCAuthSize = 0x44 + RCAuthContext = 0x45 + RCNVRange = 0x46 + RCNVSize = 0x47 + RCNVLocked = 0x48 + RCNVAuthorization = 0x49 + RCNVUninitialized = 0x4A + RCNVSpace = 0x4B + RCNVDefined = 0x4C + RCBadContext = 0x50 + RCCPHash = 0x51 + RCParent = 0x52 + RCNeedsTest = 0x53 + RCNoResult = 0x54 + RCSensitive = 0x55 +) + +var fmt0Msg = map[RCFmt0]string{ + RCInitialize: "TPM not initialized by TPM2_Startup or already initialized", + RCFailure: "commands not being accepted because of a TPM failure", + RCSequence: "improper use of a sequence handle", + RCPrivate: "not currently used", + RCHMAC: "not currently used", + RCDisabled: "the command is disabled", + RCExclusive: "command failed because audit sequence required exclusivity", + RCAuthType: "authorization handle is not correct for command", + RCAuthMissing: "5 command requires an authorization session for handle and it is not present", + RCPolicy: "policy failure in math operation or an invalid authPolicy value", + RCPCR: "PCR check fail", + RCPCRChanged: "PCR have changed since checked", + RCUpgrade: "TPM is in field upgrade mode unless called via TPM2_FieldUpgradeData(), then it is not in field upgrade mode", + RCTooManyContexts: "context ID counter is at maximum", + RCAuthUnavailable: "authValue or authPolicy is not available for selected entity", + RCReboot: "a _TPM_Init and Startup(CLEAR) is required before the TPM can resume operation", + RCUnbalanced: "the protection algorithms (hash and symmetric) are not reasonably balanced; the digest size of the hash must be larger than the key size of the symmetric algorithm", + RCCommandSize: "command commandSize value is inconsistent with contents of the command buffer; either the size is not the same as the octets loaded by the hardware interface layer or the value is not large enough to hold a command header", + RCCommandCode: "command code not supported", + RCAuthSize: "the value of authorizationSize is out of range or the number of octets in the Authorization Area is greater than required", + RCAuthContext: "use of an authorization session with a context command or another command that cannot have an authorization session", + RCNVRange: "NV offset+size is out of range", + RCNVSize: "Requested allocation size is larger than allowed", + RCNVLocked: "NV access locked", + RCNVAuthorization: "NV access authorization fails in command actions", + RCNVUninitialized: "an NV Index is used before being initialized or the state saved by TPM2_Shutdown(STATE) could not be restored", + RCNVSpace: "insufficient space for NV allocation", + RCNVDefined: "NV Index or persistent object already defined", + RCBadContext: "context in TPM2_ContextLoad() is not valid", + RCCPHash: "cpHash value already set or not correct for use", + RCParent: "handle for parent is not a valid parent", + RCNeedsTest: "some function needs testing", + RCNoResult: "returned when an internal function cannot process a request due to an unspecified problem; this code is usually related to invalid parameters that are not properly filtered by the input unmarshaling code", + RCSensitive: "the sensitive area did not unmarshal correctly after decryption", +} + +// Format 1 error codes. +const ( + RCAsymmetric = 0x01 + RCAttributes = 0x02 + RCHash = 0x03 + RCValue = 0x04 + RCHierarchy = 0x05 + RCKeySize = 0x07 + RCMGF = 0x08 + RCMode = 0x09 + RCType = 0x0A + RCHandle = 0x0B + RCKDF = 0x0C + RCRange = 0x0D + RCAuthFail = 0x0E + RCNonce = 0x0F + RCPP = 0x10 + RCScheme = 0x12 + RCSize = 0x15 + RCSymmetric = 0x16 + RCTag = 0x17 + RCSelector = 0x18 + RCInsufficient = 0x1A + RCSignature = 0x1B + RCKey = 0x1C + RCPolicyFail = 0x1D + RCIntegrity = 0x1F + RCTicket = 0x20 + RCReservedBits = 0x21 + RCBadAuth = 0x22 + RCExpired = 0x23 + RCPolicyCC = 0x24 + RCBinding = 0x25 + RCCurve = 0x26 + RCECCPoint = 0x27 +) + +var fmt1Msg = map[RCFmt1]string{ + RCAsymmetric: "asymmetric algorithm not supported or not correct", + RCAttributes: "inconsistent attributes", + RCHash: "hash algorithm not supported or not appropriate", + RCValue: "value is out of range or is not correct for the context", + RCHierarchy: "hierarchy is not enabled or is not correct for the use", + RCKeySize: "key size is not supported", + RCMGF: "mask generation function not supported", + RCMode: "mode of operation not supported", + RCType: "the type of the value is not appropriate for the use", + RCHandle: "the handle is not correct for the use", + RCKDF: "unsupported key derivation function or function not appropriate for use", + RCRange: "value was out of allowed range", + RCAuthFail: "the authorization HMAC check failed and DA counter incremented", + RCNonce: "invalid nonce size or nonce value mismatch", + RCPP: "authorization requires assertion of PP", + RCScheme: "unsupported or incompatible scheme", + RCSize: "structure is the wrong size", + RCSymmetric: "unsupported symmetric algorithm or key size, or not appropriate for instance", + RCTag: "incorrect structure tag", + RCSelector: "union selector is incorrect", + RCInsufficient: "the TPM was unable to unmarshal a value because there were not enough octets in the input buffer", + RCSignature: "the signature is not valid", + RCKey: "key fields are not compatible with the selected use", + RCPolicyFail: "a policy check failed", + RCIntegrity: "integrity check failed", + RCTicket: "invalid ticket", + RCReservedBits: "reserved bits not set to zero as required", + RCBadAuth: "authorization failure without DA implications", + RCExpired: "the policy has expired", + RCPolicyCC: "the commandCode in the policy is not the commandCode of the command or the command code in a policy command references a command that is not implemented", + RCBinding: "public and sensitive portions of an object are not cryptographically bound", + RCCurve: "curve not supported", + RCECCPoint: "point is not on the required curve", +} + +// Warning codes. +const ( + RCContextGap RCWarn = 0x01 + RCObjectMemory = 0x02 + RCSessionMemory = 0x03 + RCMemory = 0x04 + RCSessionHandles = 0x05 + RCObjectHandles = 0x06 + RCLocality = 0x07 + RCYielded = 0x08 + RCCanceled = 0x09 + RCTesting = 0x0A + RCReferenceH0 = 0x10 + RCReferenceH1 = 0x11 + RCReferenceH2 = 0x12 + RCReferenceH3 = 0x13 + RCReferenceH4 = 0x14 + RCReferenceH5 = 0x15 + RCReferenceH6 = 0x16 + RCReferenceS0 = 0x18 + RCReferenceS1 = 0x19 + RCReferenceS2 = 0x1A + RCReferenceS3 = 0x1B + RCReferenceS4 = 0x1C + RCReferenceS5 = 0x1D + RCReferenceS6 = 0x1E + RCNVRate = 0x20 + RCLockout = 0x21 + RCRetry = 0x22 + RCNVUnavailable = 0x23 +) + +var warnMsg = map[RCWarn]string{ + RCContextGap: "gap for context ID is too large", + RCObjectMemory: "out of memory for object contexts", + RCSessionMemory: "out of memory for session contexts", + RCMemory: "out of shared object/session memory or need space for internal operations", + RCSessionHandles: "out of session handles", + RCObjectHandles: "out of object handles", + RCLocality: "bad locality", + RCYielded: "the TPM has suspended operation on the command; forward progress was made and the command may be retried", + RCCanceled: "the command was canceled", + RCTesting: "TPM is performing self-tests", + RCReferenceH0: "the 1st handle in the handle area references a transient object or session that is not loaded", + RCReferenceH1: "the 2nd handle in the handle area references a transient object or session that is not loaded", + RCReferenceH2: "the 3rd handle in the handle area references a transient object or session that is not loaded", + RCReferenceH3: "the 4th handle in the handle area references a transient object or session that is not loaded", + RCReferenceH4: "the 5th handle in the handle area references a transient object or session that is not loaded", + RCReferenceH5: "the 6th handle in the handle area references a transient object or session that is not loaded", + RCReferenceH6: "the 7th handle in the handle area references a transient object or session that is not loaded", + RCReferenceS0: "the 1st authorization session handle references a session that is not loaded", + RCReferenceS1: "the 2nd authorization session handle references a session that is not loaded", + RCReferenceS2: "the 3rd authorization session handle references a session that is not loaded", + RCReferenceS3: "the 4th authorization session handle references a session that is not loaded", + RCReferenceS4: "the 5th authorization session handle references a session that is not loaded", + RCReferenceS5: "the 6th authorization session handle references a session that is not loaded", + RCReferenceS6: "the 7th authorization session handle references a session that is not loaded", + RCNVRate: "the TPM is rate-limiting accesses to prevent wearout of NV", + RCLockout: "authorizations for objects subject to DA protection are not allowed at this time because the TPM is in DA lockout mode", + RCRetry: "the TPM was not able to start the command", + RCNVUnavailable: "the command may require writing of NV and NV is not current accessible", +} + +// Indexes for arguments, handles and sessions. +const ( + RC1 RCIndex = iota + 1 + RC2 + RC3 + RC4 + RC5 + RC6 + RC7 + RC8 + RC9 + RCA + RCB + RCC + RCD + RCE + RCF +) + +const unknownCode = "unknown error code" + +// Error is returned for all Format 0 errors from the TPM. It is used for general +// errors not specific to a parameter, handle or session. +type Error struct { + Code RCFmt0 +} + +func (e Error) Error() string { + msg := fmt0Msg[e.Code] + if msg == "" { + msg = unknownCode + } + return fmt.Sprintf("error code 0x%x : %s", e.Code, msg) +} + +// VendorError represents a vendor-specific error response. These types of responses +// are not decoded and Code contains the complete response code. +type VendorError struct { + Code uint32 +} + +func (e VendorError) Error() string { + return fmt.Sprintf("vendor error code 0x%x", e.Code) +} + +// Warning is typically used to report transient errors. +type Warning struct { + Code RCWarn +} + +func (w Warning) Error() string { + msg := warnMsg[w.Code] + if msg == "" { + msg = unknownCode + } + return fmt.Sprintf("warning code 0x%x : %s", w.Code, msg) +} + +// ParameterError describes an error related to a parameter, and the parameter number. +type ParameterError struct { + Code RCFmt1 + Parameter RCIndex +} + +func (e ParameterError) Error() string { + msg := fmt1Msg[e.Code] + if msg == "" { + msg = unknownCode + } + return fmt.Sprintf("parameter %d, error code 0x%x : %s", e.Parameter, e.Code, msg) +} + +// HandleError describes an error related to a handle, and the handle number. +type HandleError struct { + Code RCFmt1 + Handle RCIndex +} + +func (e HandleError) Error() string { + msg := fmt1Msg[e.Code] + if msg == "" { + msg = unknownCode + } + return fmt.Sprintf("handle %d, error code 0x%x : %s", e.Handle, e.Code, msg) +} + +// SessionError describes an error related to a session, and the session number. +type SessionError struct { + Code RCFmt1 + Session RCIndex +} + +func (e SessionError) Error() string { + msg := fmt1Msg[e.Code] + if msg == "" { + msg = unknownCode + } + return fmt.Sprintf("session %d, error code 0x%x : %s", e.Session, e.Code, msg) +} + +// Decode a TPM2 response code and return the appropriate error. Logic +// according to the "Response Code Evaluation" chart in Part 1 of the TPM 2.0 +// spec. +func decodeResponse(code tpmutil.ResponseCode) error { + if code == tpmutil.RCSuccess { + return nil + } + if code&0x180 == 0 { // Bits 7:8 == 0 is a TPM1 error + return fmt.Errorf("response status 0x%x", code) + } + if code&0x80 == 0 { // Bit 7 unset + if code&0x400 > 0 { // Bit 10 set, vendor specific code + return VendorError{uint32(code)} + } + if code&0x800 > 0 { // Bit 11 set, warning with code in bit 0:6 + return Warning{RCWarn(code & 0x7f)} + } + // error with code in bit 0:6 + return Error{RCFmt0(code & 0x7f)} + } + if code&0x40 > 0 { // Bit 6 set, code in 0:5, parameter number in 8:11 + return ParameterError{RCFmt1(code & 0x3f), RCIndex((code & 0xf00) >> 8)} + } + if code&0x800 == 0 { // Bit 11 unset, code in 0:5, handle in 8:10 + return HandleError{RCFmt1(code & 0x3f), RCIndex((code & 0x700) >> 8)} + } + // Code in 0:5, Session in 8:10 + return SessionError{RCFmt1(code & 0x3f), RCIndex((code & 0x700) >> 8)} +} diff --git a/vendor/github.com/google/go-tpm/tpm2/error_test.go b/vendor/github.com/google/go-tpm/tpm2/error_test.go new file mode 100644 index 0000000..f0a9f41 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/error_test.go @@ -0,0 +1,36 @@ +package tpm2 + +import ( + "reflect" + "testing" + + "github.com/google/go-tpm/tpmutil" +) + +func TestError(t *testing.T) { + tests := []struct { + response tpmutil.ResponseCode + expected error + }{ + {0x501, VendorError{Code: 0x501}}, + {0x922, Warning{Code: RCRetry}}, + {0x100, Error{Code: RCInitialize}}, + {0xfc1, ParameterError{Code: RCAsymmetric, Parameter: RCF}}, + {0x7a3, HandleError{Code: RCExpired, Handle: RC7}}, + {0xfa2, SessionError{Code: RCBadAuth, Session: RC7}}, + } + + for _, test := range tests { + err := decodeResponse(test.response) + if !reflect.DeepEqual(err, test.expected) { + t.Fatalf("decodeResponse(0x%x) = %#v, want %#v", test.response, err, test.expected) + } + } +} + +// nil ReadWriter handle causes tpmutil.RunCommand to return an error. +func TestRunCommandErr(t *testing.T) { + if _, err := runCommand(nil, TagSessions, cmdSign); err == nil { + t.Error("runCommand returned nil error on error from tpmutil.RunCommand") + } +} diff --git a/vendor/github.com/google/go-tpm/tpm2/kdf.go b/vendor/github.com/google/go-tpm/tpm2/kdf.go new file mode 100644 index 0000000..e7f7966 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/kdf.go @@ -0,0 +1,70 @@ +// Copyright (c) 2018, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tpm2 + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" + "encoding/binary" + "fmt" + "hash" +) + +// KDFa implements TPM 2.0's default key derivation function, as defined in +// section 11.4.9.2 of the TPM revision 2 specification part 1. +// See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ +// The key & label parameters must not be zero length, but contextU & +// contextV may be. +// Only SHA1 & SHA256 hash algorithms are implemented at this time. +func KDFa(hashAlg Algorithm, key []byte, label string, contextU, contextV []byte, bits int) ([]byte, error) { + var counter uint32 + remaining := (bits + 7) / 8 // As per note at the bottom of page 44. + var out []byte + + var mac hash.Hash + switch hashAlg { + case AlgSHA1: + mac = hmac.New(sha1.New, key) + case AlgSHA256: + mac = hmac.New(sha256.New, key) + default: + return nil, fmt.Errorf("hash algorithm 0x%x is not supported", hashAlg) + } + + for remaining > 0 { + counter++ + if err := binary.Write(mac, binary.BigEndian, counter); err != nil { + return nil, fmt.Errorf("pack counter: %v", err) + } + mac.Write([]byte(label)) + mac.Write([]byte{0}) // Terminating null character for C-string. + mac.Write(contextU) + mac.Write(contextV) + if err := binary.Write(mac, binary.BigEndian, uint32(bits)); err != nil { + return nil, fmt.Errorf("pack bits: %v", err) + } + + out = mac.Sum(out) + remaining -= mac.Size() + mac.Reset() + } + + if len(out) > bits/8 { + out = out[:bits/8] + } + + return out, nil +} diff --git a/vendor/github.com/google/go-tpm/tpm2/open_other.go b/vendor/github.com/google/go-tpm/tpm2/open_other.go new file mode 100644 index 0000000..759ea97 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/open_other.go @@ -0,0 +1,42 @@ +// +build !windows + +// Copyright (c) 2019, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tpm2 + +import ( + "fmt" + "io" + + "github.com/google/go-tpm/tpmutil" +) + +// OpenTPM opens a channel to the TPM at the given path. If the file is a +// device, then it treats it like a normal TPM device, and if the file is a +// Unix domain socket, then it opens a connection to the socket. +func OpenTPM(path string) (io.ReadWriteCloser, error) { + rwc, err := tpmutil.OpenTPM(path) + if err != nil { + return nil, err + } + + // Make sure this is a TPM 2.0 + _, err = GetManufacturer(rwc) + if err != nil { + rwc.Close() + return nil, fmt.Errorf("open %s: device is not a TPM 2.0", path) + } + return rwc, nil +} diff --git a/vendor/github.com/google/go-tpm/tpm2/tpm2_windows_test.go b/vendor/github.com/google/go-tpm/tpm2/open_windows.go similarity index 63% rename from vendor/github.com/google/go-tpm/tpm2/tpm2_windows_test.go rename to vendor/github.com/google/go-tpm/tpm2/open_windows.go index 5dc9e86..c243fa7 100644 --- a/vendor/github.com/google/go-tpm/tpm2/tpm2_windows_test.go +++ b/vendor/github.com/google/go-tpm/tpm2/open_windows.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,22 +15,23 @@ package tpm2 import ( - "flag" + "fmt" "io" - "testing" "github.com/google/go-tpm/tpmutil" + "github.com/google/go-tpm/tpmutil/tbs" ) -var runTPMTests = flag.Bool("run_tpm_tests", false, "Run the Windows TPM integration tests. Defaults to false.") - -func openTPM(t *testing.T) io.ReadWriteCloser { - if *runTPMTests == false { - t.SkipNow() - } - rw, err := OpenTPM(tpmutil.HighPriority) +// OpenTPM opens a channel to the TPM. +func OpenTPM() (io.ReadWriteCloser, error) { + info, err := tbs.GetDeviceInfo() if err != nil { - t.Fatalf("Open TPM failed: %s\n", err) + return nil, err } - return rw + + if info.TPMVersion != tbs.TPMVersion20 { + return nil, fmt.Errorf("openTPM: device is not a TPM 2.0") + } + + return tpmutil.OpenTPM() } diff --git a/vendor/github.com/google/go-tpm/tpm2/structures.go b/vendor/github.com/google/go-tpm/tpm2/structures.go index fc54178..eb4975b 100644 --- a/vendor/github.com/google/go-tpm/tpm2/structures.go +++ b/vendor/github.com/google/go-tpm/tpm2/structures.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,9 +16,13 @@ package tpm2 import ( "bytes" + "crypto" + "crypto/ecdsa" + "crypto/rsa" "errors" "fmt" "math/big" + "reflect" "github.com/google/go-tpm/tpmutil" ) @@ -28,13 +32,13 @@ type NVPublic struct { NVIndex tpmutil.Handle NameAlg Algorithm Attributes KeyProp - AuthPolicy []byte + AuthPolicy tpmutil.U16Bytes DataSize uint16 } type tpmsSensitiveCreate struct { - UserAuth []byte - Data []byte + UserAuth tpmutil.U16Bytes + Data tpmutil.U16Bytes } // PCRSelection contains a slice of PCR indexes and a hash algorithm used in @@ -55,35 +59,114 @@ type Public struct { Type Algorithm NameAlg Algorithm Attributes KeyProp - AuthPolicy []byte + AuthPolicy tpmutil.U16Bytes - // Only one of the Parameters fields should be set. When encoding/decoding, - // one will be picked based on Type. - RSAParameters *RSAParams - ECCParameters *ECCParams + // Exactly one of the following fields should be set + // When encoding/decoding, one will be picked based on Type. + RSAParameters *RSAParams + ECCParameters *ECCParams + SymCipherParameters *SymCipherParams + KeyedHashParameters *KeyedHashParams } -func (p Public) encode() ([]byte, error) { +// Encode serializes a Public structure in TPM wire format. +func (p Public) Encode() ([]byte, error) { head, err := tpmutil.Pack(p.Type, p.NameAlg, p.Attributes, p.AuthPolicy) if err != nil { - return nil, err + return nil, fmt.Errorf("encoding Type, NameAlg, Attributes, AuthPolicy: %v", err) } var params []byte switch p.Type { case AlgRSA: params, err = p.RSAParameters.encode() + case AlgKeyedHash: + params, err = p.KeyedHashParameters.encode() case AlgECC: params, err = p.ECCParameters.encode() + case AlgSymCipher: + params, err = p.SymCipherParameters.encode() default: - err = fmt.Errorf("unsupported type in TPMT_PUBLIC: %v", p.Type) + err = fmt.Errorf("unsupported type in TPMT_PUBLIC: 0x%x", p.Type) } if err != nil { - return nil, err + return nil, fmt.Errorf("encoding RSAParameters, ECCParameters, SymCipherParameters or KeyedHash: %v", err) } return concat(head, params) } -func decodePublic(in *bytes.Buffer) (Public, error) { +// Key returns the (public) key from the public area of an object. +func (p Public) Key() (crypto.PublicKey, error) { + var pubKey crypto.PublicKey + switch p.Type { + case AlgRSA: + // Endianness of big.Int.Bytes/SetBytes and modulus in the TPM is the same + // (big-endian). + pubKey = &rsa.PublicKey{N: p.RSAParameters.Modulus(), E: int(p.RSAParameters.Exponent())} + case AlgECC: + curve, ok := toGoCurve[p.ECCParameters.CurveID] + if !ok { + return nil, fmt.Errorf("can't map TPM EC curve ID 0x%x to Go elliptic.Curve value", p.ECCParameters.CurveID) + } + pubKey = &ecdsa.PublicKey{ + X: p.ECCParameters.Point.X(), + Y: p.ECCParameters.Point.Y(), + Curve: curve, + } + default: + return nil, fmt.Errorf("unsupported public key type 0x%x", p.Type) + } + return pubKey, nil +} + +// Name computes the Digest-based Name from the public area of an object. +func (p Public) Name() (Name, error) { + pubEncoded, err := p.Encode() + if err != nil { + return Name{}, err + } + hash, err := p.NameAlg.HashConstructor() + if err != nil { + return Name{}, err + } + nameHash := hash() + nameHash.Write(pubEncoded) + return Name{ + Digest: &HashValue{ + Alg: p.NameAlg, + Value: nameHash.Sum(nil), + }, + }, nil +} + +// MatchesTemplate checks if the Public area has the same algorithms and +// parameters as the provided template. Note that this does not necessarily +// mean that the key was created from this template, as the Unique field is +// both provided in the template and overriden in the key creation process. +func (p Public) MatchesTemplate(template Public) bool { + if p.Type != template.Type || + p.NameAlg != template.NameAlg || + p.Attributes != template.Attributes || + !bytes.Equal(p.AuthPolicy, template.AuthPolicy) { + return false + } + switch p.Type { + case AlgRSA: + return p.RSAParameters.matchesTemplate(template.RSAParameters) + case AlgECC: + return p.ECCParameters.matchesTemplate(template.ECCParameters) + case AlgSymCipher: + return p.SymCipherParameters.matchesTemplate(template.SymCipherParameters) + case AlgKeyedHash: + return p.KeyedHashParameters.matchesTemplate(template.KeyedHashParameters) + default: + return true + } +} + +// DecodePublic decodes a TPMT_PUBLIC message. No error is returned if +// the input has extra trailing data. +func DecodePublic(buf []byte) (Public, error) { + in := bytes.NewBuffer(buf) var pub Public var err error if err = tpmutil.UnpackBuf(in, &pub.Type, &pub.NameAlg, &pub.Attributes, &pub.AuthPolicy); err != nil { @@ -95,8 +178,12 @@ func decodePublic(in *bytes.Buffer) (Public, error) { pub.RSAParameters, err = decodeRSAParams(in) case AlgECC: pub.ECCParameters, err = decodeECCParams(in) + case AlgSymCipher: + pub.SymCipherParameters, err = decodeSymCipherParams(in) + case AlgKeyedHash: + pub.KeyedHashParameters, err = decodeKeyedHashParams(in) default: - err = fmt.Errorf("unsupported type in TPMT_PUBLIC: %v", pub.Type) + err = fmt.Errorf("unsupported type in TPMT_PUBLIC: 0x%x", pub.Type) } return pub, err } @@ -105,16 +192,35 @@ func decodePublic(in *bytes.Buffer) (Public, error) { // // Symmetric and Sign may be nil, depending on key Attributes in Public. // -// One of Modulus and ModulusRaw must always be non-nil. Modulus takes -// precedence. ModulusRaw is used for key templates where the field named -// "unique" must be a byte array of all zeroes. +// ExponentRaw and ModulusRaw are the actual data encoded in the template, which +// is useful for templates that differ in zero-padding, for example. type RSAParams struct { - Symmetric *SymScheme - Sign *SigScheme - KeyBits uint16 - Exponent uint32 - ModulusRaw []byte - Modulus *big.Int + Symmetric *SymScheme + Sign *SigScheme + KeyBits uint16 + ExponentRaw uint32 + ModulusRaw tpmutil.U16Bytes +} + +// Exponent returns the RSA exponent value represented by ExponentRaw, handling +// the fact that an exponent of 0 represents a value of 65537 (2^16 + 1). +func (p *RSAParams) Exponent() uint32 { + if p.ExponentRaw == 0 { + return defaultRSAExponent + } + return p.ExponentRaw +} + +// Modulus returns the RSA modulus value represented by ModulusRaw, handling the +// fact that the same modulus value can have multiple different representations. +func (p *RSAParams) Modulus() *big.Int { + return new(big.Int).SetBytes(p.ModulusRaw) +} + +func (p *RSAParams) matchesTemplate(t *RSAParams) bool { + return reflect.DeepEqual(p.Symmetric, t.Symmetric) && + reflect.DeepEqual(p.Sign, t.Sign) && + p.KeyBits == t.KeyBits && p.ExponentRaw == t.ExponentRaw } func (p *RSAParams) encode() ([]byte, error) { @@ -123,33 +229,17 @@ func (p *RSAParams) encode() ([]byte, error) { } sym, err := p.Symmetric.encode() if err != nil { - return nil, err + return nil, fmt.Errorf("encoding Symmetric: %v", err) } sig, err := p.Sign.encode() if err != nil { - return nil, err - } - rest, err := tpmutil.Pack(p.KeyBits, p.Exponent) - if err != nil { - return nil, err - } - - if p.Modulus == nil && len(p.ModulusRaw) == 0 { - return nil, errors.New("RSAParams.Modulus or RSAParams.ModulusRaw must be set") - } - if p.Modulus != nil && len(p.ModulusRaw) > 0 { - return nil, errors.New("both RSAParams.Modulus and RSAParams.ModulusRaw can't be set") - } - mod := p.ModulusRaw - if p.Modulus != nil { - mod = p.Modulus.Bytes() + return nil, fmt.Errorf("encoding Sign: %v", err) } - unique, err := tpmutil.Pack(mod) + rest, err := tpmutil.Pack(p.KeyBits, p.ExponentRaw, p.ModulusRaw) if err != nil { - return nil, err + return nil, fmt.Errorf("encoding KeyBits, Exponent, Modulus: %v", err) } - - return concat(sym, sig, rest, unique) + return concat(sym, sig, rest) } func decodeRSAParams(in *bytes.Buffer) (*RSAParams, error) { @@ -157,19 +247,14 @@ func decodeRSAParams(in *bytes.Buffer) (*RSAParams, error) { var err error if params.Symmetric, err = decodeSymScheme(in); err != nil { - return nil, err + return nil, fmt.Errorf("decoding Symmetric: %v", err) } if params.Sign, err = decodeSigScheme(in); err != nil { - return nil, err + return nil, fmt.Errorf("decoding Sign: %v", err) } - var modBytes []byte - if err := tpmutil.UnpackBuf(in, ¶ms.KeyBits, ¶ms.Exponent, &modBytes); err != nil { - return nil, err - } - if params.Exponent == 0 { - params.Exponent = defaultRSAExponent + if err := tpmutil.UnpackBuf(in, ¶ms.KeyBits, ¶ms.ExponentRaw, ¶ms.ModulusRaw); err != nil { + return nil, fmt.Errorf("decoding KeyBits, Exponent, Modulus: %v", err) } - params.Modulus = new(big.Int).SetBytes(modBytes) return ¶ms, nil } @@ -184,9 +269,25 @@ type ECCParams struct { Point ECPoint } -// ECPoint represents a ECC coordinates for a point. +// ECPoint represents a ECC coordinates for a point using byte buffers. type ECPoint struct { - X, Y *big.Int + XRaw, YRaw tpmutil.U16Bytes +} + +// X returns the X Point value reprsented by XRaw. +func (p ECPoint) X() *big.Int { + return new(big.Int).SetBytes(p.XRaw) +} + +// Y returns the Y Point value reprsented by YRaw. +func (p ECPoint) Y() *big.Int { + return new(big.Int).SetBytes(p.YRaw) +} + +func (p *ECCParams) matchesTemplate(t *ECCParams) bool { + return reflect.DeepEqual(p.Symmetric, t.Symmetric) && + reflect.DeepEqual(p.Sign, t.Sign) && + p.CurveID == t.CurveID && reflect.DeepEqual(p.KDF, t.KDF) } func (p *ECCParams) encode() ([]byte, error) { @@ -195,23 +296,23 @@ func (p *ECCParams) encode() ([]byte, error) { } sym, err := p.Symmetric.encode() if err != nil { - return nil, err + return nil, fmt.Errorf("encoding Symmetric: %v", err) } sig, err := p.Sign.encode() if err != nil { - return nil, err + return nil, fmt.Errorf("encoding Sign: %v", err) } curve, err := tpmutil.Pack(p.CurveID) if err != nil { - return nil, err + return nil, fmt.Errorf("encoding CurveID: %v", err) } kdf, err := p.KDF.encode() if err != nil { - return nil, err + return nil, fmt.Errorf("encoding KDF: %v", err) } - point, err := tpmutil.Pack(p.Point.X.Bytes(), p.Point.Y.Bytes()) + point, err := tpmutil.Pack(p.Point.XRaw, p.Point.YRaw) if err != nil { - return nil, err + return nil, fmt.Errorf("encoding Point: %v", err) } return concat(sym, sig, curve, kdf, point) } @@ -221,27 +322,133 @@ func decodeECCParams(in *bytes.Buffer) (*ECCParams, error) { var err error if params.Symmetric, err = decodeSymScheme(in); err != nil { - return nil, err + return nil, fmt.Errorf("decoding Symmetric: %v", err) } if params.Sign, err = decodeSigScheme(in); err != nil { - return nil, err + return nil, fmt.Errorf("decoding Sign: %v", err) } if err := tpmutil.UnpackBuf(in, ¶ms.CurveID); err != nil { - return nil, err + return nil, fmt.Errorf("decoding CurveID: %v", err) } if params.KDF, err = decodeKDFScheme(in); err != nil { - return nil, err + return nil, fmt.Errorf("decoding KDF: %v", err) } - var x, y []byte - if err := tpmutil.UnpackBuf(in, &x, &y); err != nil { - return nil, err + if err := tpmutil.UnpackBuf(in, ¶ms.Point.XRaw, ¶ms.Point.YRaw); err != nil { + return nil, fmt.Errorf("decoding Point: %v", err) } - params.Point.X = new(big.Int).SetBytes(x) - params.Point.Y = new(big.Int).SetBytes(y) return ¶ms, nil } +// SymCipherParams represents parameters of a symmetric block cipher TPM object. +type SymCipherParams struct { + Symmetric *SymScheme + Unique tpmutil.U16Bytes +} + +func (p *SymCipherParams) matchesTemplate(t *SymCipherParams) bool { + return reflect.DeepEqual(p.Symmetric, t.Symmetric) +} + +func (p *SymCipherParams) encode() ([]byte, error) { + sym, err := p.Symmetric.encode() + if err != nil { + return nil, fmt.Errorf("encoding Symmetric: %v", err) + } + unique, err := tpmutil.Pack(p.Unique) + if err != nil { + return nil, fmt.Errorf("encoding Unique: %v", err) + } + return concat(sym, unique) +} + +func decodeSymCipherParams(in *bytes.Buffer) (*SymCipherParams, error) { + var params SymCipherParams + var err error + + if params.Symmetric, err = decodeSymScheme(in); err != nil { + return nil, fmt.Errorf("decoding Symmetric: %v", err) + } + if err := tpmutil.UnpackBuf(in, ¶ms.Unique); err != nil { + return nil, fmt.Errorf("decoding Unique: %v", err) + } + return ¶ms, nil +} + +// KeyedHashParams represents parameters of a keyed hash TPM object. +type KeyedHashParams struct { + Alg Algorithm + Hash Algorithm + KDF Algorithm + Unique tpmutil.U16Bytes +} + +func (p *KeyedHashParams) matchesTemplate(t *KeyedHashParams) bool { + if p.Alg != t.Alg { + return false + } + switch p.Alg { + case AlgHMAC: + return p.Hash == t.Hash + case AlgXOR: + return p.Hash == t.Hash && p.KDF == t.KDF + default: + return true + } +} + +func (p *KeyedHashParams) encode() ([]byte, error) { + if p == nil { + return tpmutil.Pack(AlgNull, tpmutil.U16Bytes(nil)) + } + var params []byte + var err error + switch p.Alg { + case AlgNull: + params, err = tpmutil.Pack(p.Alg) + case AlgHMAC: + params, err = tpmutil.Pack(p.Alg, p.Hash) + case AlgXOR: + params, err = tpmutil.Pack(p.Alg, p.Hash, p.KDF) + default: + err = fmt.Errorf("unsupported KeyedHash Algorithm: 0x%x", p.Alg) + } + if err != nil { + return nil, fmt.Errorf("encoding Alg Params: %v", err) + } + unique, err := tpmutil.Pack(p.Unique) + if err != nil { + return nil, fmt.Errorf("encoding Unique: %v", err) + } + return concat(params, unique) +} + +func decodeKeyedHashParams(in *bytes.Buffer) (*KeyedHashParams, error) { + var p KeyedHashParams + var err error + if err = tpmutil.UnpackBuf(in, &p.Alg); err != nil { + return nil, fmt.Errorf("decoding Alg: %v", err) + } + switch p.Alg { + case AlgNull: + err = nil + case AlgHMAC: + err = tpmutil.UnpackBuf(in, &p.Hash) + case AlgXOR: + err = tpmutil.UnpackBuf(in, &p.Hash, &p.KDF) + default: + err = fmt.Errorf("unsupported KeyedHash Algorithm: 0x%x", p.Alg) + } + if err != nil { + return nil, fmt.Errorf("decoding Alg Params: %v", err) + } + if err = tpmutil.UnpackBuf(in, &p.Unique); err != nil { + return nil, fmt.Errorf("decoding Unique: %v", err) + } + return &p, nil +} + // SymScheme represents a symmetric encryption scheme. +// Known in the specification by TPMT_SYM_DEF_OBJECT. type SymScheme struct { Alg Algorithm KeyBits uint16 @@ -258,17 +465,33 @@ func (s *SymScheme) encode() ([]byte, error) { func decodeSymScheme(in *bytes.Buffer) (*SymScheme, error) { var scheme SymScheme if err := tpmutil.UnpackBuf(in, &scheme.Alg); err != nil { - return nil, err + return nil, fmt.Errorf("decoding Alg: %v", err) } if scheme.Alg == AlgNull { return nil, nil } if err := tpmutil.UnpackBuf(in, &scheme.KeyBits, &scheme.Mode); err != nil { - return nil, err + return nil, fmt.Errorf("decoding KeyBits, Mode: %v", err) } return &scheme, nil } +// AsymScheme represents am asymmetric encryption scheme. +type AsymScheme struct { + Alg Algorithm + Hash Algorithm +} + +func (s *AsymScheme) encode() ([]byte, error) { + if s == nil || s.Alg.IsNull() { + return tpmutil.Pack(AlgNull) + } + if s.Alg.UsesHash() { + return tpmutil.Pack(s.Alg, s.Hash) + } + return tpmutil.Pack(s.Alg) +} + // SigScheme represents a signing scheme. type SigScheme struct { Alg Algorithm @@ -289,17 +512,17 @@ func (s *SigScheme) encode() ([]byte, error) { func decodeSigScheme(in *bytes.Buffer) (*SigScheme, error) { var scheme SigScheme if err := tpmutil.UnpackBuf(in, &scheme.Alg); err != nil { - return nil, err + return nil, fmt.Errorf("decoding Alg: %v", err) } if scheme.Alg == AlgNull { return nil, nil } if err := tpmutil.UnpackBuf(in, &scheme.Hash); err != nil { - return nil, err + return nil, fmt.Errorf("decoding Hash: %v", err) } if scheme.Alg.UsesCount() { if err := tpmutil.UnpackBuf(in, &scheme.Count); err != nil { - return nil, err + return nil, fmt.Errorf("decoding Count: %v", err) } } return &scheme, nil @@ -321,13 +544,13 @@ func (s *KDFScheme) encode() ([]byte, error) { func decodeKDFScheme(in *bytes.Buffer) (*KDFScheme, error) { var scheme KDFScheme if err := tpmutil.UnpackBuf(in, &scheme.Alg); err != nil { - return nil, err + return nil, fmt.Errorf("decoding Alg: %v", err) } if scheme.Alg == AlgNull { return nil, nil } if err := tpmutil.UnpackBuf(in, &scheme.Hash); err != nil { - return nil, err + return nil, fmt.Errorf("decoding Hash: %v", err) } return &scheme, nil } @@ -340,22 +563,23 @@ type Signature struct { ECC *SignatureECC } -func decodeSignature(in *bytes.Buffer) (*Signature, error) { +// DecodeSignature decodes a serialized TPMT_SIGNATURE structure. +func DecodeSignature(in *bytes.Buffer) (*Signature, error) { var sig Signature if err := tpmutil.UnpackBuf(in, &sig.Alg); err != nil { - return nil, err + return nil, fmt.Errorf("decoding Alg: %v", err) } switch sig.Alg { - case AlgRSASSA: + case AlgRSASSA, AlgRSAPSS: sig.RSA = new(SignatureRSA) if err := tpmutil.UnpackBuf(in, sig.RSA); err != nil { - return nil, err + return nil, fmt.Errorf("decoding RSA: %v", err) } case AlgECDSA: sig.ECC = new(SignatureECC) - var r, s []byte + var r, s tpmutil.U16Bytes if err := tpmutil.UnpackBuf(in, &sig.ECC.HashAlg, &r, &s); err != nil { - return nil, err + return nil, fmt.Errorf("decoding ECC: %v", err) } sig.ECC.R = big.NewInt(0).SetBytes(r) sig.ECC.S = big.NewInt(0).SetBytes(s) @@ -368,7 +592,7 @@ func decodeSignature(in *bytes.Buffer) (*Signature, error) { // SignatureRSA is an RSA-specific signature value. type SignatureRSA struct { HashAlg Algorithm - Signature []byte + Signature tpmutil.U16Bytes } // SignatureECC is an ECC-specific signature value. @@ -381,12 +605,13 @@ type SignatureECC struct { // Private contains private section of a TPM key. type Private struct { Type Algorithm - AuthValue []byte - SeedValue []byte - Sensitive []byte + AuthValue tpmutil.U16Bytes + SeedValue tpmutil.U16Bytes + Sensitive tpmutil.U16Bytes } -func (p Private) encode() ([]byte, error) { +// Encode serializes a Private structure in TPM wire format. +func (p Private) Encode() ([]byte, error) { if p.Type.IsNull() { return nil, nil } @@ -400,13 +625,15 @@ type tpmtSigScheme struct { // AttestationData contains data attested by TPM commands (like Certify). type AttestationData struct { - Magic uint32 - Type tpmutil.Tag - QualifiedSigner Name - ExtraData []byte - ClockInfo ClockInfo - FirmwareVersion uint64 - AttestedCertifyInfo *CertifyInfo + Magic uint32 + Type tpmutil.Tag + QualifiedSigner Name + ExtraData tpmutil.U16Bytes + ClockInfo ClockInfo + FirmwareVersion uint64 + AttestedCertifyInfo *CertifyInfo + AttestedQuoteInfo *QuoteInfo + AttestedCreationInfo *CreationInfo } // DecodeAttestationData decode a TPMS_ATTEST message. No error is returned if @@ -418,7 +645,7 @@ func DecodeAttestationData(in []byte) (*AttestationData, error) { if err := tpmutil.UnpackBuf(buf, &ad.Magic, &ad.Type); err != nil { return nil, fmt.Errorf("decoding Magic/Type: %v", err) } - n, err := decodeName(buf) + n, err := DecodeName(buf) if err != nil { return nil, fmt.Errorf("decoding QualifiedSigner: %v", err) } @@ -428,17 +655,99 @@ func DecodeAttestationData(in []byte) (*AttestationData, error) { } // The spec specifies several other types of attestation data. We only need - // parsing of Certify attestation data for now. If you need support for - // other attestation types, add them here. - if ad.Type != tagAttestCertify { - return nil, fmt.Errorf("only Certify attestation structure is supported, got type 0x%x", ad.Type) - } - if ad.AttestedCertifyInfo, err = decodeCertifyInfo(buf); err != nil { - return nil, fmt.Errorf("decoding AttestedCertifyInfo: %v", err) + // parsing of Certify & Creation attestation data for now. If you need + // support for other attestation types, add them here. + switch ad.Type { + case TagAttestCertify: + if ad.AttestedCertifyInfo, err = decodeCertifyInfo(buf); err != nil { + return nil, fmt.Errorf("decoding AttestedCertifyInfo: %v", err) + } + case TagAttestCreation: + if ad.AttestedCreationInfo, err = decodeCreationInfo(buf); err != nil { + return nil, fmt.Errorf("decoding AttestedCreationInfo: %v", err) + } + case TagAttestQuote: + if ad.AttestedQuoteInfo, err = decodeQuoteInfo(buf); err != nil { + return nil, fmt.Errorf("decoding AttestedQuoteInfo: %v", err) + } + default: + return nil, fmt.Errorf("only Certify & Creation attestation structures are supported, got type 0x%x", ad.Type) } + return &ad, nil } +// Encode serializes an AttestationData structure in TPM wire format. +func (ad AttestationData) Encode() ([]byte, error) { + head, err := tpmutil.Pack(ad.Magic, ad.Type) + if err != nil { + return nil, fmt.Errorf("encoding Magic, Type: %v", err) + } + signer, err := ad.QualifiedSigner.Encode() + if err != nil { + return nil, fmt.Errorf("encoding QualifiedSigner: %v", err) + } + tail, err := tpmutil.Pack(ad.ExtraData, ad.ClockInfo, ad.FirmwareVersion) + if err != nil { + return nil, fmt.Errorf("encoding ExtraData, ClockInfo, FirmwareVersion: %v", err) + } + + var info []byte + switch ad.Type { + case TagAttestCertify: + if info, err = ad.AttestedCertifyInfo.encode(); err != nil { + return nil, fmt.Errorf("encoding AttestedCertifyInfo: %v", err) + } + case TagAttestCreation: + if info, err = ad.AttestedCreationInfo.encode(); err != nil { + return nil, fmt.Errorf("encoding AttestedCreationInfo: %v", err) + } + default: + return nil, fmt.Errorf("only Certify & Creation attestation structures are supported, got type 0x%x", ad.Type) + } + + return concat(head, signer, tail, info) +} + +// CreationInfo contains Creation-specific data for TPMS_ATTEST. +type CreationInfo struct { + Name Name + // Most TPM2B_Digest structures contain a TPMU_HA structure + // and get parsed to HashValue. This is never the case for the + // digest in TPMS_CREATION_INFO. + OpaqueDigest tpmutil.U16Bytes +} + +func decodeCreationInfo(in *bytes.Buffer) (*CreationInfo, error) { + var ci CreationInfo + + n, err := DecodeName(in) + if err != nil { + return nil, fmt.Errorf("decoding Name: %v", err) + } + ci.Name = *n + + if err := tpmutil.UnpackBuf(in, &ci.OpaqueDigest); err != nil { + return nil, fmt.Errorf("decoding Digest: %v", err) + } + + return &ci, nil +} + +func (ci CreationInfo) encode() ([]byte, error) { + n, err := ci.Name.Encode() + if err != nil { + return nil, fmt.Errorf("encoding Name: %v", err) + } + + d, err := tpmutil.Pack(ci.OpaqueDigest) + if err != nil { + return nil, fmt.Errorf("encoding Digest: %v", err) + } + + return concat(n, d) +} + // CertifyInfo contains Certify-specific data for TPMS_ATTEST. type CertifyInfo struct { Name Name @@ -448,13 +757,13 @@ type CertifyInfo struct { func decodeCertifyInfo(in *bytes.Buffer) (*CertifyInfo, error) { var ci CertifyInfo - n, err := decodeName(in) + n, err := DecodeName(in) if err != nil { return nil, fmt.Errorf("decoding Name: %v", err) } ci.Name = *n - n, err = decodeName(in) + n, err = DecodeName(in) if err != nil { return nil, fmt.Errorf("decoding QualifiedName: %v", err) } @@ -463,15 +772,125 @@ func decodeCertifyInfo(in *bytes.Buffer) (*CertifyInfo, error) { return &ci, nil } -// Name contains a name for TPM entities. Only one of Handle/Digest should be -// set. +func (ci CertifyInfo) encode() ([]byte, error) { + n, err := ci.Name.Encode() + if err != nil { + return nil, fmt.Errorf("encoding Name: %v", err) + } + qn, err := ci.QualifiedName.Encode() + if err != nil { + return nil, fmt.Errorf("encoding QualifiedName: %v", err) + } + return concat(n, qn) +} + +// QuoteInfo represents a TPMS_QUOTE_INFO structure. +type QuoteInfo struct { + PCRSelection PCRSelection + PCRDigest tpmutil.U16Bytes +} + +func decodeQuoteInfo(in *bytes.Buffer) (*QuoteInfo, error) { + var out QuoteInfo + sel, err := decodeOneTPMLPCRSelection(in) + if err != nil { + return nil, fmt.Errorf("decoding PCRSelection: %v", err) + } + out.PCRSelection = sel + + if err := tpmutil.UnpackBuf(in, &out.PCRDigest); err != nil { + return nil, fmt.Errorf("decoding PCRDigest: %v", err) + } + return &out, nil +} + +// IDObject represents an encrypted credential bound to a TPM object. +type IDObject struct { + IntegrityHMAC tpmutil.U16Bytes + // EncIdentity is packed raw, as the bytes representing the size + // of the credential value are present within the encrypted blob. + EncIdentity tpmutil.RawBytes +} + +// CreationData describes the attributes and environment for an object created +// on the TPM. This structure encodes/decodes to/from TPMS_CREATION_DATA. +type CreationData struct { + PCRSelection PCRSelection + PCRDigest tpmutil.U16Bytes + Locality byte + ParentNameAlg Algorithm + ParentName Name + ParentQualifiedName Name + OutsideInfo tpmutil.U16Bytes +} + +func (cd *CreationData) encode() ([]byte, error) { + sel, err := encodeTPMLPCRSelection(cd.PCRSelection) + if err != nil { + return nil, fmt.Errorf("encoding PCRSelection: %v", err) + } + d, err := tpmutil.Pack(cd.PCRDigest, cd.Locality, cd.ParentNameAlg) + if err != nil { + return nil, fmt.Errorf("encoding PCRDigest, Locality, ParentNameAlg: %v", err) + } + pn, err := cd.ParentName.Encode() + if err != nil { + return nil, fmt.Errorf("encoding ParentName: %v", err) + } + pqn, err := cd.ParentQualifiedName.Encode() + if err != nil { + return nil, fmt.Errorf("encoding ParentQualifiedName: %v", err) + } + o, err := tpmutil.Pack(cd.OutsideInfo) + if err != nil { + return nil, fmt.Errorf("encoding OutsideInfo: %v", err) + } + return concat(sel, d, pn, pqn, o) +} + +// DecodeCreationData decodes a TPMS_CREATION_DATA message. No error is +// returned if the input has extra trailing data. +func DecodeCreationData(buf []byte) (*CreationData, error) { + in := bytes.NewBuffer(buf) + var out CreationData + + sel, err := decodeOneTPMLPCRSelection(in) + if err != nil { + return nil, fmt.Errorf("decodeOneTPMLPCRSelection returned error %v", err) + } + out.PCRSelection = sel + + if err := tpmutil.UnpackBuf(in, &out.PCRDigest, &out.Locality, &out.ParentNameAlg); err != nil { + return nil, fmt.Errorf("decoding PCRDigest, Locality, ParentNameAlg: %v", err) + } + + n, err := DecodeName(in) + if err != nil { + return nil, fmt.Errorf("decoding ParentName: %v", err) + } + out.ParentName = *n + if n, err = DecodeName(in); err != nil { + return nil, fmt.Errorf("decoding ParentQualifiedName: %v", err) + } + out.ParentQualifiedName = *n + + if err := tpmutil.UnpackBuf(in, &out.OutsideInfo); err != nil { + return nil, fmt.Errorf("decoding OutsideInfo: %v", err) + } + + return &out, nil +} + +// Name represents a TPM2B_NAME, a name for TPM entities. Only one of +// Handle or Digest should be set. type Name struct { Handle *tpmutil.Handle Digest *HashValue } -func decodeName(in *bytes.Buffer) (*Name, error) { - var nameBuf []byte +// DecodeName deserializes a Name hash from the TPM wire format. +func DecodeName(in *bytes.Buffer) (*Name, error) { + var nameBuf tpmutil.U16Bytes if err := tpmutil.UnpackBuf(in, &nameBuf); err != nil { return nil, err } @@ -495,32 +914,43 @@ func decodeName(in *bytes.Buffer) (*Name, error) { return name, nil } +// Encode serializes a Name hash into the TPM wire format. +func (n Name) Encode() ([]byte, error) { + var buf []byte + var err error + switch { + case n.Handle != nil: + if buf, err = tpmutil.Pack(*n.Handle); err != nil { + return nil, fmt.Errorf("encoding Handle: %v", err) + } + case n.Digest != nil: + if buf, err = n.Digest.Encode(); err != nil { + return nil, fmt.Errorf("encoding Digest: %v", err) + } + default: + // Name is empty, which is valid. + } + return tpmutil.Pack(tpmutil.U16Bytes(buf)) +} + // MatchesPublic compares Digest in Name against given Public structure. Note: // this only works for regular Names, not Qualified Names. func (n Name) MatchesPublic(p Public) (bool, error) { - buf, err := p.encode() - if err != nil { - return false, err - } if n.Digest == nil { return false, errors.New("Name doesn't have a Digest, can't compare to Public") } - hfn, ok := hashConstructors[n.Digest.Alg] - if !ok { - return false, fmt.Errorf("Name hash algorithm 0x%x not supported", n.Digest.Alg) + expected, err := p.Name() + if err != nil { + return false, err } - - h := hfn() - h.Write(buf) - digest := h.Sum(nil) - - return bytes.Equal(digest, n.Digest.Value), nil + // No secrets, so no constant-time comparison + return bytes.Equal(expected.Digest.Value, n.Digest.Value), nil } // HashValue is an algorithm-specific hash value. type HashValue struct { Alg Algorithm - Value []byte + Value tpmutil.U16Bytes } func decodeHashValue(in *bytes.Buffer) (*HashValue, error) { @@ -532,13 +962,18 @@ func decodeHashValue(in *bytes.Buffer) (*HashValue, error) { if !ok { return nil, fmt.Errorf("unsupported hash algorithm type 0x%x", hv.Alg) } - hv.Value = make([]byte, hfn().Size()) + hv.Value = make(tpmutil.U16Bytes, hfn().Size()) if _, err := in.Read(hv.Value); err != nil { return nil, fmt.Errorf("decoding Value: %v", err) } return &hv, nil } +// Encode represents the given hash value as a TPMT_HA structure. +func (hv HashValue) Encode() ([]byte, error) { + return tpmutil.Pack(hv.Alg, tpmutil.RawBytes(hv.Value)) +} + // ClockInfo contains TPM state info included in AttestationData. type ClockInfo struct { Clock uint64 @@ -546,3 +981,43 @@ type ClockInfo struct { RestartCount uint32 Safe byte } + +// AlgorithmAttributes represents a TPMA_ALGORITHM value. +type AlgorithmAttributes uint32 + +// AlgorithmDescription represents a TPMS_ALGORITHM_DESCRIPTION structure. +type AlgorithmDescription struct { + ID Algorithm + Attributes AlgorithmAttributes +} + +// TaggedProperty represents a TPMS_TAGGED_PROPERTY structure. +type TaggedProperty struct { + Tag TPMProp + Value uint32 +} + +// Ticket represents evidence the TPM previously processed +// information. +type Ticket struct { + Type tpmutil.Tag + Hierarchy uint32 + Digest tpmutil.U16Bytes +} + +func decodeTicket(in *bytes.Buffer) (*Ticket, error) { + var t Ticket + if err := tpmutil.UnpackBuf(in, &t.Type, &t.Hierarchy, &t.Digest); err != nil { + return nil, fmt.Errorf("decoding Type, Hierarchy, Digest: %v", err) + } + return &t, nil +} + +// AuthCommand represents a TPMS_AUTH_COMMAND. This structure encapsulates parameters +// which authorize the use of a given handle or parameter. +type AuthCommand struct { + Session tpmutil.Handle + Nonce tpmutil.U16Bytes + Attributes SessionAttributes + Auth tpmutil.U16Bytes +} diff --git a/vendor/github.com/google/go-tpm/tpm2/test/benchmark_test.go b/vendor/github.com/google/go-tpm/tpm2/test/benchmark_test.go new file mode 100644 index 0000000..b9c0295 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/test/benchmark_test.go @@ -0,0 +1,91 @@ +// Copyright (c) 2018, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tpm2 + +import ( + "crypto/sha256" + "testing" + + . "github.com/google/go-tpm/tpm2" +) + +func BenchmarkRSA2048Signing(b *testing.B) { + b.StopTimer() + rw := openTPM(b) + defer rw.Close() + + pub := Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA256, + }, + KeyBits: 2048, + }, + } + + signerHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, pub) + if err != nil { + b.Fatalf("CreatePrimary failed: %v", err) + } + defer FlushContext(rw, signerHandle) + + digest := sha256.Sum256([]byte("randomString")) + + b.StartTimer() + for i := 0; i < b.N; i++ { + if _, err := Sign(rw, signerHandle, defaultPassword, digest[:], pub.RSAParameters.Sign); err != nil { + b.Fatalf("Signing failed: %v", err) + } + } +} + +func BenchmarkECCNISTP256Signing(b *testing.B) { + b.StopTimer() + rw := openTPM(b) + defer rw.Close() + skipOnUnsupportedAlg(b, rw, AlgECC) + + pub := Public{ + Type: AlgECC, + NameAlg: AlgSHA256, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + ECCParameters: &ECCParams{ + Sign: &SigScheme{ + Alg: AlgECDSA, + Hash: AlgSHA256, + }, + CurveID: CurveNISTP256, + }, + } + + signerHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, pub) + if err != nil { + b.Fatalf("CreatePrimary failed: %v", err) + } + defer FlushContext(rw, signerHandle) + + digest := sha256.Sum256([]byte("randomString")) + + b.StartTimer() + for i := 0; i < b.N; i++ { + if _, err := Sign(rw, signerHandle, defaultPassword, digest[:], pub.ECCParameters.Sign); err != nil { + b.Fatalf("Signing failed: %v", err) + } + } +} diff --git a/vendor/github.com/google/go-tpm/tpm2/test/kdf_test.go b/vendor/github.com/google/go-tpm/tpm2/test/kdf_test.go new file mode 100644 index 0000000..812b769 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/test/kdf_test.go @@ -0,0 +1,72 @@ +// Copyright (c) 2018, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tpm2 + +import ( + "bytes" + "testing" + + . "github.com/google/go-tpm/tpm2" +) + +func TestKDFa(t *testing.T) { + tcs := []struct { + hashAlg Algorithm + key []byte + contextU []byte + contextV []byte + label string + bits int + expected []byte + }{ + { + hashAlg: AlgSHA256, + key: []byte{'y', 'o', 'l', 'o', 0}, + contextU: []byte{'k', 'e', 'k', 0}, + contextV: []byte{'y', 'o', 'y', 'o', 0}, + label: "IDENTITY", + bits: 128, + expected: []byte{0xd2, 0xd7, 0x2c, 0xc7, 0xa8, 0xa5, 0xeb, 0x09, 0xe8, 0xc7, 0x90, 0x12, 0xe2, 0xda, 0x9f, 0x22}, + }, + { + hashAlg: AlgSHA256, + key: []byte{'c', 'a', 0}, + contextU: []byte{'a', 'b', 'c', 0}, + label: "IDENTITY", + bits: 1024, + expected: []byte{0x1a, 0xae, 0x71, 0x51, 0xac, 0x1a, 0x56, 0x90, 0xed, 0xa7, 0xdc, 0xab, 0xd5, 0x68, 0x00, 0xc1, 0x1c, 0x56, 0xa3, 0x81, 0x0b, 0xa0, 0x59, 0x82, 0x6f, 0xe4, 0x77, 0x63, 0x48, 0xd6, 0xae, 0x8e, 0x5d, 0x5d, 0x18, 0xc7, 0xcc, 0xf8, 0x37, 0x3f, 0x7b, 0x94, 0x2a, 0xda, 0x8b, 0x91, 0x2b, 0x12, 0xda, 0x56, 0xfb, 0x37, 0xf6, 0x4b, 0x93, 0x58, 0x72, 0x84, 0x1e, 0xc0, 0x7d, 0x38, 0xe1, 0xfb, 0x8e, 0x7e, 0xc8, 0x6e, 0xfc, 0xbf, 0xb4, 0x44, 0x75, 0x6b, 0xc8, 0x86, 0x3f, 0x85, 0x8d, 0x26, 0x90, 0xa6, 0x21, 0xc9, 0xaf, 0xb9, 0x83, 0xcd, 0x77, 0xe7, 0xa1, 0x04, 0x8a, 0xe1, 0xa7, 0x59, 0x8a, 0xc8, 0x95, 0x32, 0x3d, 0x44, 0xc1, 0x02, 0x27, 0xaf, 0x0a, 0x00, 0x14, 0x4c, 0xab, 0x55, 0x11, 0x10, 0x75, 0xdc, 0x6b, 0x72, 0xad, 0x6e, 0xb1, 0x63, 0xc7, 0x45, 0x8b, 0x87, 0x8e, 0x8c}, + }, + { + hashAlg: AlgSHA1, + key: []byte{'c', 'a', 0}, + contextU: []byte{'a', 'b', 'c', 0}, + label: "IDENTITY", + bits: 256, + expected: []byte{0x83, 0xf3, 0x54, 0xaf, 0xcf, 0x92, 0x3d, 0xe2, 0x11, 0x2e, 0x08, 0x91, 0x43, 0x4c, 0xd0, 0xbd, 0xc8, 0xac, 0xbf, 0x01, 0xb8, 0x11, 0xc0, 0xe8, 0xcd, 0x06, 0x2d, 0xed, 0x39, 0xe3, 0x1f, 0x7f}, + }, + } + + for _, tc := range tcs { + o, err := KDFa(tc.hashAlg, tc.key, tc.label, tc.contextU, tc.contextV, tc.bits) + if err != nil { + t.Fatalf("KDFa(%v, %v, %q, %v, %v, %v) returned error: %v", tc.hashAlg, tc.key, tc.label, tc.contextU, tc.contextV, tc.bits, err) + } + if !bytes.Equal(tc.expected, o) { + t.Errorf("Test with KDFa(%v, %v, %q, %v, %v, %v) returned incorrect result", tc.hashAlg, tc.key, tc.label, tc.contextU, tc.contextV, tc.bits) + t.Logf(" Got: %v", o) + t.Logf(" Want: %v", tc.expected) + } + } +} diff --git a/vendor/github.com/google/go-tpm/tpm2/tpm2_linux_test.go b/vendor/github.com/google/go-tpm/tpm2/test/tpm2_other_test.go similarity index 69% rename from vendor/github.com/google/go-tpm/tpm2/tpm2_linux_test.go rename to vendor/github.com/google/go-tpm/tpm2/test/tpm2_other_test.go index 3a08a23..bb4825a 100644 --- a/vendor/github.com/google/go-tpm/tpm2/tpm2_linux_test.go +++ b/vendor/github.com/google/go-tpm/tpm2/test/tpm2_other_test.go @@ -1,4 +1,6 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// +build !windows + +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,17 +20,19 @@ import ( "flag" "io" "testing" + + . "github.com/google/go-tpm/tpm2" ) -var tpmPath = flag.String("tpm_path", "", "Path to TPM character device. Most Linux systems expose it under /dev/tpm0. Empty value (default) will disable all integration tests.") +var tpmPath = flag.String("tpm-path", "", "Path to TPM character device. Most Linux systems expose it under /dev/tpm0. Empty value (default) will disable all integration tests.") -func openTPM(t *testing.T) io.ReadWriteCloser { - if *tpmPath == "" { - t.SkipNow() - } +func useDeviceTPM() bool { return *tpmPath != "" } + +func openDeviceTPM(tb testing.TB) io.ReadWriteCloser { + tb.Helper() rw, err := OpenTPM(*tpmPath) if err != nil { - t.Fatalf("Open TPM at %s failed: %s\n", *tpmPath, err) + tb.Fatalf("Open TPM at %s failed: %s\n", *tpmPath, err) } return rw } diff --git a/vendor/github.com/google/go-tpm/tpm2/test/tpm2_test.go b/vendor/github.com/google/go-tpm/tpm2/test/tpm2_test.go new file mode 100644 index 0000000..d978445 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/test/tpm2_test.go @@ -0,0 +1,1406 @@ +// Copyright (c) 2018, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tpm2 + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/sha256" + "io" + "reflect" + "strings" + "testing" + + "github.com/google/go-tpm-tools/simulator" + . "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpmutil" +) + +func openTPM(tb testing.TB) io.ReadWriteCloser { + tb.Helper() + if useDeviceTPM() { + return openDeviceTPM(tb) + } + simulator, err := simulator.Get() + if err != nil { + tb.Fatalf("Simulator initialization failed: %v", err) + } + return simulator +} + +var ( + // PCR7 is for SecureBoot. + pcrSelection0 = PCRSelection{Hash: AlgSHA1, PCRs: []int{0}} + pcrSelection1 = PCRSelection{Hash: AlgSHA1, PCRs: []int{1}} + pcrSelection7 = PCRSelection{Hash: AlgSHA1, PCRs: []int{7}} + defaultKeyParams = Public{ + Type: AlgRSA, + NameAlg: AlgSHA1, + Attributes: FlagStorageDefault, + RSAParameters: &RSAParams{ + Symmetric: &SymScheme{ + Alg: AlgAES, + KeyBits: 128, + Mode: AlgCFB, + }, + KeyBits: 2048, + ExponentRaw: 1<<16 + 1, + }, + } + defaultPassword = "\x01\x02\x03\x04" + emptyPassword = "" +) + +func TestGetRandom(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + if _, err := GetRandom(rw, 16); err != nil { + t.Fatalf("GetRandom failed: %v", err) + } +} + +func TestReadPCRs(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + pcrs, err := ReadPCRs(rw, pcrSelection7) + if err != nil { + t.Errorf("ReadPCRs failed: %s", err) + } + if !useDeviceTPM() { + return // PCR 7 is initialized to 0 in the simulator. + } + for pcr, val := range pcrs { + if empty := make([]byte, len(val)); reflect.DeepEqual(empty, val) { + t.Errorf("Value of PCR %d is empty", pcr) + } + } +} + +func TestReadClock(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + if _, _, err := ReadClock(rw); err != nil { + t.Fatalf("ReadClock failed: %s", err) + } + +} + +func TestGetCapability(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + for _, tt := range []struct { + capa Capability + count uint32 + property uint32 + typ interface{} + }{ + {CapabilityHandles, 1, uint32(HandleTypeTransient) << 24, tpmutil.Handle(0)}, + {CapabilityAlgs, 1, 0, AlgorithmDescription{}}, + {CapabilityTPMProperties, 1, uint32(NVMaxBufferSize), TaggedProperty{}}, + } { + l, _, err := GetCapability(rw, tt.capa, tt.count, tt.property) + if err != nil { + t.Fatalf("GetCapability(%v, %d, %d) = _, %v; want _, nil", tt.capa, tt.count, tt.property, err) + } + for _, i := range l { + if reflect.TypeOf(i) != reflect.TypeOf(tt.typ) { + t.Fatalf("GetCapability(%v, %d, %d) returned an element with the wrong type: %v; want %v", tt.capa, tt.count, tt.property, reflect.TypeOf(i), reflect.TypeOf(tt.typ)) + } + } + } +} + +func TestCombinedKeyTest(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + parentHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, defaultKeyParams) + if err != nil { + t.Fatalf("CreatePrimary failed: %s", err) + } + defer FlushContext(rw, parentHandle) + + privateBlob, publicBlob, _, _, _, err := CreateKey(rw, parentHandle, pcrSelection7, defaultPassword, defaultPassword, defaultKeyParams) + if err != nil { + t.Fatalf("CreateKey failed: %s", err) + } + + keyHandle, _, err := Load(rw, parentHandle, defaultPassword, publicBlob, privateBlob) + if err != nil { + t.Fatalf("Load failed: %s", err) + } + defer FlushContext(rw, keyHandle) + + if _, _, _, err := ReadPublic(rw, keyHandle); err != nil { + t.Fatalf("ReadPublic failed: %s", err) + } +} + +func TestCombinedEndorsementTest(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + parentHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) + if err != nil { + t.Fatalf("CreatePrimary failed: %s", err) + } + defer FlushContext(rw, parentHandle) + + privateBlob, publicBlob, _, _, _, err := CreateKey(rw, parentHandle, pcrSelection7, emptyPassword, defaultPassword, defaultKeyParams) + if err != nil { + t.Fatalf("CreateKey failed: %s", err) + } + + keyHandle, _, err := Load(rw, parentHandle, emptyPassword, publicBlob, privateBlob) + if err != nil { + t.Fatalf("Load failed: %s", err) + } + defer FlushContext(rw, keyHandle) + + _, name, _, err := ReadPublic(rw, keyHandle) + if err != nil { + t.Fatalf("ReadPublic failed: %s", err) + } + + // Generate Credential + credential := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10} + credBlob, encryptedSecret0, err := MakeCredential(rw, parentHandle, credential, name) + if err != nil { + t.Fatalf("MakeCredential failed: %v", err) + } + + recoveredCredential1, err := ActivateCredential(rw, keyHandle, parentHandle, defaultPassword, emptyPassword, credBlob, encryptedSecret0) + if err != nil { + t.Fatalf("ActivateCredential failed: %v", err) + } + if !bytes.Equal(credential, recoveredCredential1) { + t.Fatalf("Credential and recovered credential differ: got %v, want %v", recoveredCredential1, credential) + } + + recoveredCredential2, err := ActivateCredentialUsingAuth(rw, []AuthCommand{ + {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(defaultPassword)}, + {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(emptyPassword)}, + }, keyHandle, parentHandle, credBlob, encryptedSecret0) + if err != nil { + t.Fatalf("ActivateCredentialWithAuth failed: %v", err) + } + if !bytes.Equal(credential, recoveredCredential2) { + t.Errorf("Credential and recovered credential differ: got %v, want %v", recoveredCredential2, credential) + } + + _, err = ActivateCredentialUsingAuth(rw, []AuthCommand{ + {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte("incorrect password")}, + {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(emptyPassword)}, + }, keyHandle, parentHandle, credBlob, encryptedSecret0) + if err == nil { + t.Fatal("ActivateCredentialUsingAuth: error == nil, expected authorization failure") + } + if !strings.Contains(err.Error(), "the authorization HMAC check failed") { + t.Errorf("ActivateCredentialUsingAuth: error = %v, expected authorization failure", err) + } + + _, err = ActivateCredentialUsingAuth(rw, []AuthCommand{ + {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(emptyPassword)}, + }, keyHandle, parentHandle, credBlob, encryptedSecret0) + if err == nil { + t.Fatal("ActivateCredentialUsingAuth: error == nil, expected response status 0x98e (authorization failure)") + } + if !strings.Contains(err.Error(), "len(auth) = 1, want 2") { + t.Errorf("ActivateCredentialUsingAuth: error = %v, expected len(auth) = 1, want 2", err) + } +} + +func TestCreatePrimaryEx(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + keyHandle, pub1, creation, _, _, name, err := CreatePrimaryEx(rw, HandleOwner, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) + if err != nil { + t.Fatalf("CreatePrimary failed: %v", err) + } + defer FlushContext(rw, keyHandle) + + pub, _, _, err := ReadPublic(rw, keyHandle) + if err != nil { + t.Fatalf("ReadPublic failed: %s", err) + } + pub2, err := pub.Encode() + if err != nil { + t.Fatalf("Failed to encode public: %v", err) + } + + if !bytes.Equal(pub1, pub2) { + t.Error("Mismatch between public returned from CreatePrimaryEx() & ReadPublic()") + t.Logf("CreatePrimaryEx: %v", pub1) + t.Logf("ReadPublic: %v", pub2) + } + + if _, err := DecodeName(bytes.NewBuffer(name)); err != nil { + t.Errorf("Failed to decode name: %v", err) + } + if _, err := DecodeCreationData(creation); err != nil { + t.Fatalf("DecodeCreationData() returned err: %v", err) + } +} + +func TestCombinedContextTest(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + rootHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) + if err != nil { + t.Fatalf("CreatePrimary failed: %v", err) + } + defer FlushContext(rw, rootHandle) + + // CreateKey (Quote Key) + quotePrivate, quotePublic, _, _, _, err := CreateKey(rw, rootHandle, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) + if err != nil { + t.Fatalf("CreateKey failed: %v", err) + } + + quoteHandle, _, err := Load(rw, rootHandle, emptyPassword, quotePublic, quotePrivate) + if err != nil { + t.Fatalf("Load failed: %v", err) + } + defer FlushContext(rw, quoteHandle) + + saveArea, err := ContextSave(rw, quoteHandle) + if err != nil { + t.Fatalf("ContextSave failed: %v", err) + } + FlushContext(rw, quoteHandle) + + quoteHandle, err = ContextLoad(rw, saveArea) + if err != nil { + t.Fatalf("Load failed: %v", err) + } +} + +func TestEvictControl(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + rootHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) + if err != nil { + t.Fatalf("CreatePrimary failed: %v", err) + } + defer FlushContext(rw, rootHandle) + + // CreateKey (Quote Key) + quotePrivate, quotePublic, _, _, _, err := CreateKey(rw, rootHandle, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) + if err != nil { + t.Fatalf("CreateKey failed: %v", err) + } + + quoteHandle, _, err := Load(rw, rootHandle, emptyPassword, quotePublic, quotePrivate) + if err != nil { + t.Fatalf("Load failed: %v", err) + } + defer FlushContext(rw, quoteHandle) + + persistentHandle := tpmutil.Handle(0x817FFFFF) + // Evict persistent key, if there is one already (e.g. last test run failed). + if err := EvictControl(rw, emptyPassword, HandleOwner, persistentHandle, persistentHandle); err != nil { + t.Logf("(expected) EvictControl failed: %v", err) + } + // Make key persistent. + if err := EvictControl(rw, emptyPassword, HandleOwner, quoteHandle, persistentHandle); err != nil { + t.Fatalf("EvictControl failed: %v", err) + } + // Evict persistent key. + if err := EvictControl(rw, emptyPassword, HandleOwner, persistentHandle, persistentHandle); err != nil { + t.Fatalf("EvictControl failed: %v", err) + } +} + +func TestHash(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + val := []byte("garmonbozia") + got, err := Hash(rw, AlgSHA256, val) + if err != nil { + t.Fatalf("Hash failed: %v", err) + } + want := sha256.Sum256(val) + + if !bytes.Equal(got, want[:]) { + t.Errorf("Hash(%q) returned %x, want %x", val, got, want) + } +} + +func skipOnUnsupportedAlg(t testing.TB, rw io.ReadWriter, alg Algorithm) { + moreData := true + for i := uint32(0); moreData; i++ { + var err error + var descs []interface{} + descs, moreData, err = GetCapability(rw, CapabilityAlgs, 1, i) + if err != nil { + t.Fatalf("Could not get TPM algorithm capability: %v", err) + } + for _, desc := range descs { + if desc.(AlgorithmDescription).ID == alg { + return + } + } + if !moreData { + break + } + } + t.Skipf("Algorithm %v is not supported by the TPM", alg) +} + +func TestLoadExternalPublicKey(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + run := func(t *testing.T, public Public, private Private) { + t.Helper() + + h, _, err := LoadExternal(rw, public, private, HandleNull) + if err != nil { + t.Fatal(err) + } + defer FlushContext(rw, h) + } + + t.Run("RSA", func(t *testing.T) { + pk, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + rp := Public{ + Type: AlgRSA, + NameAlg: AlgSHA1, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA1, + }, + KeyBits: 2048, + ExponentRaw: uint32(pk.PublicKey.E), + ModulusRaw: pk.PublicKey.N.Bytes(), + }, + } + private := Private{ + Type: AlgRSA, + Sensitive: pk.Primes[0].Bytes(), + } + run(t, rp, private) + }) + t.Run("ECC", func(t *testing.T) { + skipOnUnsupportedAlg(t, rw, AlgECC) + pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + public := Public{ + Type: AlgECC, + NameAlg: AlgSHA1, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + ECCParameters: &ECCParams{ + Sign: &SigScheme{ + Alg: AlgECDSA, + Hash: AlgSHA1, + }, + CurveID: CurveNISTP256, + Point: ECPoint{XRaw: pk.PublicKey.X.Bytes(), YRaw: pk.PublicKey.Y.Bytes()}, + }, + } + private := Private{ + Type: AlgECC, + Sensitive: pk.D.Bytes(), + } + run(t, public, private) + }) +} + +func TestCertify(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + params := Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagSignerDefault, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA256, + }, + KeyBits: 2048, + }, + } + signerHandle, signerPub, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, params) + if err != nil { + t.Fatalf("CreatePrimary(signer) failed: %s", err) + } + defer FlushContext(rw, signerHandle) + + subjectHandle, subjectPub, err := CreatePrimary(rw, HandlePlatform, pcrSelection7, emptyPassword, defaultPassword, params) + if err != nil { + t.Fatalf("CreatePrimary(subject) failed: %s", err) + } + defer FlushContext(rw, subjectHandle) + + attest, sig, err := Certify(rw, defaultPassword, defaultPassword, subjectHandle, signerHandle, nil) + if err != nil { + t.Errorf("Certify failed: %s", err) + return + } + + attestHash := sha256.Sum256(attest) + if err := rsa.VerifyPKCS1v15(signerPub.(*rsa.PublicKey), crypto.SHA256, attestHash[:], sig); err != nil { + t.Errorf("Signature verification failed: %v", err) + } + + t.Run("DecodeAttestationData", func(t *testing.T) { + ad, err := DecodeAttestationData(attest) + if err != nil { + t.Fatal("DecodeAttestationData:", err) + } + params := Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagSignerDefault, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA256, + }, + KeyBits: 2048, + // Note: we don't include Exponent because CreatePrimary also + // returns Public without it. + ModulusRaw: subjectPub.(*rsa.PublicKey).N.Bytes(), + }, + } + matches, err := ad.AttestedCertifyInfo.Name.MatchesPublic(params) + if err != nil { + t.Fatalf("AttestedCertifyInfo.Name.MatchesPublic error: %v", err) + } + if !matches { + t.Error("Name in AttestationData doesn't match Public structure of subject") + } + }) +} + +func TestCertifyExternalKey(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + params := Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagSignerDefault, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA256, + }, + KeyBits: 2048, + }, + } + signerHandle, signerPub, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, params) + if err != nil { + t.Fatalf("CreatePrimary(signer) failed: %s", err) + } + defer FlushContext(rw, signerHandle) + + run := func(t *testing.T, public Public, private Private) { + t.Helper() + subjectHandle, _, err := LoadExternal(rw, public, private, HandleNull) + if err != nil { + t.Fatalf("LoadExternal: %v", err) + } + defer FlushContext(rw, subjectHandle) + + attest, sig, err := Certify(rw, emptyPassword, defaultPassword, subjectHandle, signerHandle, nil) + if err != nil { + t.Errorf("Certify failed: %s", err) + return + } + + attestHash := sha256.Sum256(attest) + if err := rsa.VerifyPKCS1v15(signerPub.(*rsa.PublicKey), crypto.SHA256, attestHash[:], sig); err != nil { + t.Errorf("Signature verification failed: %v", err) + } + } + t.Run("RSA", func(t *testing.T) { + pk, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + public := Public{ + Type: AlgRSA, + NameAlg: AlgSHA1, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA1, + }, + KeyBits: 2048, + ExponentRaw: uint32(pk.PublicKey.E), + ModulusRaw: pk.PublicKey.N.Bytes(), + }, + } + private := Private{ + Type: AlgRSA, + Sensitive: pk.Primes[0].Bytes(), + } + run(t, public, private) + }) + t.Run("ECC", func(t *testing.T) { + skipOnUnsupportedAlg(t, rw, AlgECC) + pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + public := Public{ + Type: AlgECC, + NameAlg: AlgSHA1, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + ECCParameters: &ECCParams{ + Sign: &SigScheme{ + Alg: AlgECDSA, + Hash: AlgSHA1, + }, + CurveID: CurveNISTP256, + Point: ECPoint{XRaw: pk.PublicKey.X.Bytes(), YRaw: pk.PublicKey.Y.Bytes()}, + }, + } + private := Private{ + Type: AlgECC, + Sensitive: pk.D.Bytes(), + } + run(t, public, private) + }) +} + +func TestSign(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + run := func(t *testing.T, pub Public) { + signerHandle, signerPub, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, pub) + if err != nil { + t.Fatalf("CreatePrimary failed: %s", err) + } + defer FlushContext(rw, signerHandle) + + digest := sha256.Sum256([]byte("heyo")) + + var scheme *SigScheme + if pub.RSAParameters != nil { + scheme = pub.RSAParameters.Sign + } + if pub.ECCParameters != nil { + scheme = pub.ECCParameters.Sign + } + sig, err := Sign(rw, signerHandle, defaultPassword, digest[:], scheme) + if err != nil { + t.Fatalf("Sign failed: %s", err) + } + switch signerPub := signerPub.(type) { + case *rsa.PublicKey: + switch scheme.Alg { + case AlgRSASSA: + if err := rsa.VerifyPKCS1v15(signerPub, crypto.SHA256, digest[:], sig.RSA.Signature); err != nil { + t.Errorf("Signature verification failed: %v", err) + } + case AlgRSAPSS: + if err := rsa.VerifyPSS(signerPub, crypto.SHA256, digest[:], sig.RSA.Signature, nil); err != nil { + t.Errorf("Signature verification failed: %v", err) + } + default: + t.Errorf("unsupported signature algorithm 0x%x", scheme.Alg) + } + case *ecdsa.PublicKey: + if !ecdsa.Verify(signerPub, digest[:], sig.ECC.R, sig.ECC.S) { + t.Error("Signature verification failed") + } + } + } + + t.Run("RSA SSA", func(t *testing.T) { + run(t, Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA256, + }, + KeyBits: 2048, + }, + }) + }) + t.Run("RSA PSS", func(t *testing.T) { + run(t, Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSAPSS, + Hash: AlgSHA256, + }, + KeyBits: 2048, + }, + }) + }) + t.Run("ECC", func(t *testing.T) { + skipOnUnsupportedAlg(t, rw, AlgECC) + run(t, Public{ + Type: AlgECC, + NameAlg: AlgSHA256, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + ECCParameters: &ECCParams{ + Sign: &SigScheme{ + Alg: AlgECDSA, + Hash: AlgSHA256, + }, + CurveID: CurveNISTP256, + }, + }) + }) +} + +func TestPCREvent(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + debugPCR := uint32(16) + arbitraryBytes := []byte{1} + if err := PCREvent(rw, tpmutil.Handle(debugPCR), arbitraryBytes); err != nil { + t.Fatal(err) + } +} + +func TestPCRExtend(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + tests := []struct { + desc string + hashAlg Algorithm + hashSize int + hashSum func([]byte) []byte + }{ + { + desc: "SHA1", + hashAlg: AlgSHA1, + hashSize: sha1.Size, + hashSum: func(in []byte) []byte { + s := sha1.Sum(in) + return s[:] + }, + }, + { + desc: "SHA256", + hashAlg: AlgSHA256, + hashSize: sha256.Size, + hashSum: func(in []byte) []byte { + s := sha256.Sum256(in) + return s[:] + }, + }, + } + + const pcr = int(16) + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + pcrValue := bytes.Repeat([]byte{0xF}, tt.hashSize) + oldPCRValue, err := ReadPCR(rw, pcr, tt.hashAlg) + if err != nil { + t.Fatalf("Can't read PCR %d from the TPM: %s", pcr, err) + } + + if err = PCRExtend(rw, tpmutil.Handle(pcr), tt.hashAlg, pcrValue, ""); err != nil { + t.Fatalf("Failed to extend PCR %d: %s", pcr, err) + } + + newPCRValue, err := ReadPCR(rw, pcr, tt.hashAlg) + if err != nil { + t.Fatalf("Can't read PCR %d from the TPM: %s", pcr, err) + } + + finalPCR := tt.hashSum(append(oldPCRValue, pcrValue...)) + + if !bytes.Equal(finalPCR, newPCRValue) { + t.Fatalf("PCRs not equal, got %x, want %x", finalPCR, newPCRValue) + } + }) + } +} + +func TestReadPCR(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + pcrVal, err := ReadPCR(rw, 16 /*pcr*/, AlgSHA256) + if err != nil { + t.Fatal(err) + } + if len(pcrVal) != 32 { + t.Fatalf("Expected a 32 byte PCR value but got: %v", pcrVal) + } +} + +func makeAttestationData() AttestationData { + signer := tpmutil.Handle(100) + return AttestationData{ + Magic: 1, + QualifiedSigner: Name{ + Handle: &signer, + }, + ExtraData: []byte("foo"), + ClockInfo: ClockInfo{ + Clock: 3, + ResetCount: 4, + RestartCount: 5, + Safe: 6, + }, + FirmwareVersion: 7, + } +} + +func TestEncodeDecodeCertifyAttestationData(t *testing.T) { + ciQualifiedName := tpmutil.Handle(101) + ad := makeAttestationData() + ad.Type = TagAttestCertify + ad.AttestedCertifyInfo = &CertifyInfo{ + Name: Name{ + Digest: &HashValue{ + Alg: AlgSHA1, + Value: make([]byte, crypto.SHA1.Size()), + }, + }, + QualifiedName: Name{ + Handle: &ciQualifiedName, + }, + } + + encoded, err := ad.Encode() + if err != nil { + t.Fatalf("error encoding AttestationData: %v", err) + } + decoded, err := DecodeAttestationData(encoded) + if err != nil { + t.Fatalf("error decoding AttestationData: %v", err) + } + + if !reflect.DeepEqual(*decoded, ad) { + t.Errorf("got decoded value:\n%v\nwant:\n%v", decoded, ad) + } +} + +func TestEncodeDecodeCreationAttestationData(t *testing.T) { + ad := makeAttestationData() + ad.Type = TagAttestCreation + ad.AttestedCreationInfo = &CreationInfo{ + Name: Name{ + Digest: &HashValue{ + Alg: AlgSHA1, + Value: make([]byte, crypto.SHA1.Size()), + }, + }, + OpaqueDigest: []byte{7, 8, 9}, + } + + encoded, err := ad.Encode() + if err != nil { + t.Fatalf("error encoding AttestationData: %v", err) + } + decoded, err := DecodeAttestationData(encoded) + if err != nil { + t.Fatalf("error decoding AttestationData: %v", err) + } + + if !reflect.DeepEqual(*decoded, ad) { + t.Errorf("got decoded value:\n%v\nwant:\n%v", decoded, ad) + } +} + +func TestEncodeDecodePublicDefaultRSAExponent(t *testing.T) { + p := Public{ + Type: AlgRSA, + NameAlg: AlgSHA1, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA1, + }, + KeyBits: 2048, + ExponentRaw: 1<<16 + 1, + ModulusRaw: []byte{1, 2, 3, 4, 7, 8, 9, 9}, + }, + } + + e, err := p.Encode() + if err != nil { + t.Fatalf("Public{%+v}.Encode() returned error: %v", p, err) + } + d, err := DecodePublic(e) + if err != nil { + t.Fatalf("DecodePublic(%v) returned error: %v", e, err) + } + if !reflect.DeepEqual(p, d) { + t.Errorf("RSA TPMT_PUBLIC with default exponent changed after being encoded+decoded") + t.Logf("\tGot: %+v", d) + t.Logf("\tWant: %+v", p) + } +} + +func TestCreateAndCertifyCreation(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + params := Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagSignerDefault | FlagNoDA, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA256, + }, + KeyBits: 2048, + }, + } + keyHandle, pub, _, creationHash, tix, _, err := CreatePrimaryEx(rw, HandleEndorsement, pcrSelection7, emptyPassword, emptyPassword, params) + if err != nil { + t.Fatalf("CreatePrimaryEx failed: %s", err) + } + defer FlushContext(rw, keyHandle) + attestation, signature, err := CertifyCreation(rw, emptyPassword, keyHandle, keyHandle, nil, creationHash, SigScheme{AlgRSASSA, AlgSHA256, 0}, tix) + if err != nil { + t.Fatalf("CertifyCreation failed: %s", err) + } + att, err := DecodeAttestationData(attestation) + if err != nil { + t.Fatalf("DecodeAttestationData(%v) failed: %v", attestation, err) + } + if att.Type != TagAttestCreation { + t.Errorf("Got att.Type = %v, want TagAttestCreation", att.Type) + } + p, err := DecodePublic(pub) + if err != nil { + t.Fatalf("DecodePublic failed: %v", err) + } + match, err := att.AttestedCreationInfo.Name.MatchesPublic(p) + if err != nil { + t.Fatalf("MatchesPublic failed: %v", err) + } + if !match { + t.Error("Attested name does not match returned public key.") + t.Logf("Name: %v", att.AttestedCreationInfo.Name) + t.Logf("Public: %v", p) + } + rsaPub := rsa.PublicKey{E: int(p.RSAParameters.Exponent()), N: p.RSAParameters.Modulus()} + hsh := crypto.SHA256.New() + hsh.Write(attestation) + if err := rsa.VerifyPKCS1v15(&rsaPub, crypto.SHA256, hsh.Sum(nil), signature); err != nil { + t.Errorf("VerifyPKCS1v15 failed: %v", err) + } +} + +func TestNVReadWrite(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + var ( + idx tpmutil.Handle = 0x1500000 + data = []byte("testdata") + attr = AttrOwnerWrite | AttrOwnerRead + ) + + // Undefine the space, just in case the previous run of this test failed + // to clean up. + if err := NVUndefineSpace(rw, emptyPassword, HandleOwner, idx); err != nil { + t.Logf("(not a failure) NVUndefineSpace at index 0x%x failed: %v", idx, err) + } + + // Define space in NV storage and clean up afterwards or subsequent runs will fail. + if err := NVDefineSpace(rw, + HandleOwner, + idx, + emptyPassword, + emptyPassword, + nil, + attr, + uint16(len(data)), + ); err != nil { + t.Fatalf("NVDefineSpace failed: %v", err) + } + defer NVUndefineSpace(rw, emptyPassword, HandleOwner, idx) + + // Write the data + if err := NVWrite(rw, HandleOwner, idx, emptyPassword, data, 0); err != nil { + t.Fatalf("NVWrite failed: %v", err) + } + + // Make sure the public area of the index can be read + pub, err := NVReadPublic(rw, idx) + if err != nil { + t.Fatalf("NVReadPublic failed: %v", err) + } + if int(pub.DataSize) != len(data) { + t.Fatalf("public NV data size mismatch, got %d, want %d, ", pub.DataSize, len(data)) + } + + // Read all of the data with NVReadEx and compare to what was written + outdata, err := NVReadEx(rw, idx, HandleOwner, emptyPassword, 0) + if err != nil { + t.Fatalf("NVReadEx failed: %v", err) + } + if !bytes.Equal(data, outdata) { + t.Fatalf("data read from NV index does not match, got %x, want %x", outdata, data) + } +} + +func TestPolicySecret(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + sessHandle, _, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) + if err != nil { + t.Fatalf("StartAuthSession() failed: %v", err) + } + defer FlushContext(rw, sessHandle) + + if _, err := PolicySecret(rw, HandleEndorsement, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}, sessHandle, nil, nil, nil, 0); err != nil { + t.Fatalf("PolicySecret() failed: %v", err) + } +} + +func TestQuote(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + params := Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagSignerDefault | FlagNoDA, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA256, + }, + KeyBits: 2048, + }, + } + keyHandle, pub, _, _, _, _, err := CreatePrimaryEx(rw, HandleEndorsement, pcrSelection7, emptyPassword, emptyPassword, params) + if err != nil { + t.Fatalf("CreatePrimaryEx failed: %s", err) + } + defer FlushContext(rw, keyHandle) + + attestation, signature, err := Quote(rw, keyHandle, emptyPassword, emptyPassword, nil, pcrSelection7, AlgNull) + if err != nil { + t.Fatalf("Quote failed: %v", err) + } + + att, err := DecodeAttestationData(attestation) + if err != nil { + t.Fatalf("DecodeAttestationData(%v) failed: %v", attestation, err) + } + if att.Type != TagAttestQuote { + t.Errorf("Got att.Type = %v, want TagAttestQuote", att.Type) + } + if att.AttestedQuoteInfo == nil { + t.Error("AttestedQuoteInfo = nil, want non-nil") + } + p, err := DecodePublic(pub) + if err != nil { + t.Fatalf("DecodePublic failed: %v", err) + } + rsaPub := rsa.PublicKey{E: int(p.RSAParameters.Exponent()), N: p.RSAParameters.Modulus()} + hsh := crypto.SHA256.New() + hsh.Write(attestation) + if err := rsa.VerifyPKCS1v15(&rsaPub, crypto.SHA256, hsh.Sum(nil), signature.RSA.Signature); err != nil { + t.Errorf("VerifyPKCS1v15 failed: %v", err) + } +} + +func TestReadPublicKey(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + run := func(t *testing.T, public Public, private Private, pubKeyIn crypto.PublicKey) { + t.Helper() + + h, _, err := LoadExternal(rw, public, private, HandleNull) + if err != nil { + t.Fatal(err) + } + defer FlushContext(rw, h) + + pub, _, _, err := ReadPublic(rw, h) + if err != nil { + t.Fatalf("ReadPublic failed: %s", err) + } + + pubKeyOut, err := pub.Key() + if err != nil { + t.Fatalf("Public.Key() failed: %s", err) + } + + if !reflect.DeepEqual(pubKeyIn, pubKeyOut) { + t.Fatalf("Public.Key() = %#v; want %#v", pubKeyOut, pubKeyIn) + } + } + + t.Run("RSA", func(t *testing.T) { + pk, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + rp := Public{ + Type: AlgRSA, + NameAlg: AlgSHA1, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA1, + }, + KeyBits: 2048, + ExponentRaw: uint32(pk.PublicKey.E), + ModulusRaw: pk.PublicKey.N.Bytes(), + }, + } + private := Private{ + Type: AlgRSA, + Sensitive: pk.Primes[0].Bytes(), + } + run(t, rp, private, &pk.PublicKey) + }) + t.Run("ECC", func(t *testing.T) { + skipOnUnsupportedAlg(t, rw, AlgECC) + pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + public := Public{ + Type: AlgECC, + NameAlg: AlgSHA1, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + ECCParameters: &ECCParams{ + Sign: &SigScheme{ + Alg: AlgECDSA, + Hash: AlgSHA1, + }, + CurveID: CurveNISTP256, + Point: ECPoint{XRaw: pk.PublicKey.X.Bytes(), YRaw: pk.PublicKey.Y.Bytes()}, + }, + } + private := Private{ + Type: AlgECC, + Sensitive: pk.D.Bytes(), + } + run(t, public, private, &pk.PublicKey) + }) +} + +func TestEncryptDecrypt(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + parentHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagRestricted | FlagDecrypt | FlagUserWithAuth | FlagFixedParent | FlagFixedTPM | FlagSensitiveDataOrigin, + RSAParameters: &RSAParams{ + Symmetric: &SymScheme{ + Alg: AlgAES, + KeyBits: 128, + Mode: AlgCFB, + }, + KeyBits: 2048, + }, + }) + if err != nil { + t.Fatalf("CreatePrimary failed: %s", err) + } + defer FlushContext(rw, parentHandle) + privateBlob, publicBlob, _, _, _, err := CreateKey(rw, parentHandle, pcrSelection7, defaultPassword, defaultPassword, Public{ + Type: AlgSymCipher, + NameAlg: AlgSHA256, + Attributes: FlagDecrypt | FlagSign | FlagUserWithAuth | FlagFixedParent | FlagFixedTPM | FlagSensitiveDataOrigin, + SymCipherParameters: &SymCipherParams{ + Symmetric: &SymScheme{ + Alg: AlgAES, + KeyBits: 128, + Mode: AlgCFB, + }, + }, + }) + if err != nil { + t.Fatalf("CreateKey failed: %s", err) + } + key, _, err := Load(rw, parentHandle, defaultPassword, publicBlob, privateBlob) + if err != nil { + t.Fatalf("Load failed: %s", err) + } + defer FlushContext(rw, key) + + data := bytes.Repeat([]byte("a"), 1e4) // 10KB + iv := make([]byte, 16) + + encrypted, err := EncryptSymmetric(rw, defaultPassword, key, iv, data) + if err != nil { + t.Fatalf("EncryptSymmetric failed: %s", err) + } + if bytes.Equal(encrypted, data) { + t.Error("encrypted blob matches unenecrypted data") + } + decrypted, err := DecryptSymmetric(rw, defaultPassword, key, iv, encrypted) + if err != nil { + t.Fatalf("DecryptSymmetric failed: %s", err) + } + if !bytes.Equal(decrypted, data) { + t.Errorf("got decrypted data: %q, want: %q", decrypted, data) + } +} + +func TestRSAEncryptDecrypt(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + handle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagDecrypt | FlagUserWithAuth | FlagFixedParent | FlagFixedTPM | FlagSensitiveDataOrigin, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgNull, + Hash: AlgNull, + }, + KeyBits: 2048, + }, + }) + if err != nil { + t.Fatalf("CreatePrimary failed: %s", err) + } + defer FlushContext(rw, handle) + + tests := map[string]struct { + scheme *AsymScheme + data []byte + label string + }{ + "No padding": { + scheme: &AsymScheme{Alg: AlgNull}, + data: bytes.Repeat([]byte("a"), 256), + }, + "RSAES-PKCS1": { + scheme: &AsymScheme{Alg: AlgRSAES}, + data: bytes.Repeat([]byte("a"), 245), + }, + "RSAES-OAEP-SHA1": { + scheme: &AsymScheme{Alg: AlgOAEP, Hash: AlgSHA1}, + data: bytes.Repeat([]byte("a"), 214), + }, + "RSAES-OAEP-SHA256": { + scheme: &AsymScheme{Alg: AlgOAEP, Hash: AlgSHA256}, + data: bytes.Repeat([]byte("a"), 190), + }, + "RSAES-OAEP-SHA256 with label": { + scheme: &AsymScheme{Alg: AlgOAEP, Hash: AlgSHA256}, + data: bytes.Repeat([]byte("a"), 190), + label: "label", + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + encrypted, err := RSAEncrypt(rw, handle, test.data, test.scheme, test.label) + if err != nil { + t.Fatal("RSAEncrypt failed:", err) + } + decrypted, err := RSADecrypt(rw, handle, defaultPassword, encrypted, test.scheme, test.label) + if err != nil { + t.Fatal("RSADecrypt failed:", err) + } + if !bytes.Equal(decrypted, test.data) { + t.Errorf("got decrypted data: %q, want: %q", decrypted, test.data) + } + }) + } + +} + +func TestCreatePrimaryRawTemplate(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + pubRaw, err := defaultKeyParams.Encode() + if err != nil { + t.Fatal(err) + } + kh, pub, err := CreatePrimaryRawTemplate(rw, HandleEndorsement, PCRSelection{}, "", "", pubRaw) + if err != nil { + t.Fatalf("CreatePrimary failed: %v", err) + } + defer FlushContext(rw, kh) + + pubRSA, ok := pub.(*rsa.PublicKey) + if !ok { + t.Fatalf("got public key type %T, want *rsa.PublicKey", pub) + } + gotKeySize, wantKeySize := pubRSA.Size(), int(defaultKeyParams.RSAParameters.KeyBits/8) + if gotKeySize != wantKeySize { + t.Errorf("got key size %v, want %v", gotKeySize, wantKeySize) + } + if pubRSA.E != int(defaultKeyParams.RSAParameters.Exponent()) { + t.Errorf("got key exponent %v, want %v", pubRSA.E, defaultKeyParams.RSAParameters.Exponent()) + } +} + +func TestMatchesTemplate(t *testing.T) { + tests := []struct { + name string + makePublic func() Public + goodChange func(*Public) + badChange func(*Public) + }{ + { + "RSA", + func() Public { + return Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagSignerDefault, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA256, + }, + KeyBits: 2048, + }, + } + }, + func(pub *Public) { pub.RSAParameters.ModulusRaw = make([]byte, 256) }, + func(pub *Public) { pub.RSAParameters.KeyBits = 1024 }, + }, + { + "ECC", + func() Public { + return Public{ + Type: AlgECC, + NameAlg: AlgSHA256, + Attributes: FlagSignerDefault, + ECCParameters: &ECCParams{ + Sign: &SigScheme{ + Alg: AlgECDSA, + Hash: AlgSHA256, + }, + CurveID: CurveNISTP256, + }, + } + }, + func(pub *Public) { pub.ECCParameters.Point.XRaw = make([]byte, 32) }, + func(pub *Public) { pub.ECCParameters.CurveID = CurveNISTP384 }, + }, + { + "SymCipher", + func() Public { + return Public{ + Type: AlgSymCipher, + NameAlg: AlgSHA256, + Attributes: FlagSignerDefault, + SymCipherParameters: &SymCipherParams{ + Symmetric: &SymScheme{ + Alg: AlgAES, + KeyBits: 128, + Mode: AlgCFB, + }, + }, + } + }, + func(pub *Public) { pub.SymCipherParameters.Unique = make([]byte, 256) }, + func(pub *Public) { pub.SymCipherParameters.Symmetric.KeyBits = 256 }, + }, + { + "KeyedHash", + func() Public { + return Public{ + Type: AlgKeyedHash, + NameAlg: AlgSHA256, + Attributes: FlagSignerDefault, + KeyedHashParameters: &KeyedHashParams{ + Alg: AlgHMAC, + Hash: AlgSHA256, + }, + } + }, + func(pub *Public) { pub.KeyedHashParameters.Unique = make([]byte, 256) }, + func(pub *Public) { pub.KeyedHashParameters.Hash = AlgSHA1 }, + }, + { + "TypeMismatch", + func() Public { + return Public{ + Type: AlgKeyedHash, + NameAlg: AlgSHA256, + Attributes: FlagSignerDefault, + KeyedHashParameters: &KeyedHashParams{Alg: AlgNull}, + } + }, + func(pub *Public) { pub.KeyedHashParameters.Unique = make([]byte, 256) }, + func(pub *Public) { pub.Type = AlgRSA }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + template := test.makePublic() + pub := test.makePublic() + + test.goodChange(&pub) + if !pub.MatchesTemplate(template) { + t.Error("Change should not cause template mismatch") + } + + encTmpl, err := template.Encode() + if err != nil { + t.Fatal(err) + } + decTmpl, err := DecodePublic(encTmpl) + if err != nil { + t.Fatal(err) + } + if !pub.MatchesTemplate(decTmpl) { + t.Error("Encoding/Decoding should not cause template mismatch") + } + + test.badChange(&pub) + if pub.MatchesTemplate(template) { + t.Error("Change should cause template mismatch") + } + }) + } +} diff --git a/vendor/github.com/google/go-tpm/tpm2/test/tpm2_windows_test.go b/vendor/github.com/google/go-tpm/tpm2/test/tpm2_windows_test.go new file mode 100644 index 0000000..4ecbe4a --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/test/tpm2_windows_test.go @@ -0,0 +1,34 @@ +// Copyright (c) 2018, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tpm2 + +import ( + "flag" + "io" + "testing" +) + +var runTPMTests = flag.Bool("use-tbs", false, "Run integration tests against Windows TPM Base Services (TBS). Defaults to false.") + +func useDeviceTPM() bool { return *runTPMTests } + +func openDeviceTPM(tb testing.TB) io.ReadWriteCloser { + tb.Helper() + rw, err := OpenTPM() + if err != nil { + tb.Fatalf("Open TPM failed: %s\n", err) + } + return rw +} diff --git a/vendor/github.com/google/go-tpm/tpm2/tpm2.go b/vendor/github.com/google/go-tpm/tpm2/tpm2.go index e714fd2..9615e7c 100644 --- a/vendor/github.com/google/go-tpm/tpm2/tpm2.go +++ b/vendor/github.com/google/go-tpm/tpm2/tpm2.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,27 +18,20 @@ package tpm2 import ( "bytes" "crypto" - "crypto/ecdsa" - "crypto/rsa" "fmt" "io" "github.com/google/go-tpm/tpmutil" ) -// OpenTPM opens a channel to the TPM at the given path. If the file is a -// device, then it treats it like a normal TPM device, and if the file is a -// Unix domain socket, then it opens a connection to the socket. -var OpenTPM = tpmutil.OpenTPM - // GetRandom gets random bytes from the TPM. -func GetRandom(rw io.ReadWriteCloser, size uint16) ([]byte, error) { - resp, err := runCommand(rw, tagNoSessions, cmdGetRandom, size) +func GetRandom(rw io.ReadWriter, size uint16) ([]byte, error) { + resp, err := runCommand(rw, TagNoSessions, cmdGetRandom, size) if err != nil { return nil, err } - var randBytes []byte + var randBytes tpmutil.U16Bytes if _, err := tpmutil.Unpack(resp, &randBytes); err != nil { return nil, err } @@ -49,12 +42,12 @@ func GetRandom(rw io.ReadWriteCloser, size uint16) ([]byte, error) { // the TPM. This must be called for any loaded handle to avoid out-of-memory // errors in TPM. func FlushContext(rw io.ReadWriter, handle tpmutil.Handle) error { - _, err := runCommand(rw, tagNoSessions, cmdFlushContext, handle) + _, err := runCommand(rw, TagNoSessions, cmdFlushContext, handle) return err } -func encodeTPMLPCRSelection(sel PCRSelection) ([]byte, error) { - if len(sel.PCRs) == 0 { +func encodeTPMLPCRSelection(sel ...PCRSelection) ([]byte, error) { + if len(sel) == 0 { return tpmutil.Pack(uint32(0)) } @@ -66,54 +59,92 @@ func encodeTPMLPCRSelection(sel PCRSelection) ([]byte, error) { // For example, selecting PCRs 3 and 9 looks like: // size(3) mask mask mask // 00000011 00000000 00000001 00000100 - ts := tpmsPCRSelection{ - Hash: sel.Hash, - Size: sizeOfPCRSelect, - PCRs: make([]byte, sizeOfPCRSelect), + var retBytes []byte + for _, s := range sel { + if len(s.PCRs) == 0 { + return tpmutil.Pack(uint32(0)) + } + + ts := tpmsPCRSelection{ + Hash: s.Hash, + Size: sizeOfPCRSelect, + PCRs: make(tpmutil.RawBytes, sizeOfPCRSelect), + } + + // s[i].PCRs parameter is indexes of PCRs, convert that to set bits. + for _, n := range s.PCRs { + byteNum := n / 8 + bytePos := byte(1 << byte(n%8)) + ts.PCRs[byteNum] |= bytePos + } + + tmpBytes, err := tpmutil.Pack(ts) + if err != nil { + return nil, err + } + + retBytes = append(retBytes, tmpBytes...) } - // sel.PCRs parameter is indexes of PCRs, convert that to set bits. - for _, n := range sel.PCRs { - byteNum := n / 8 - bytePos := byte(1 << byte(n%8)) - ts.PCRs[byteNum] |= bytePos + tmpSize, err := tpmutil.Pack(uint32(len(sel))) + if err != nil { + return nil, err } - // Only encode 1 TPMS_PCR_SELECT value. - return tpmutil.Pack(uint32(1), ts) + retBytes = append(tmpSize, retBytes...) + + return retBytes, nil } -func decodeTPMLPCRSelection(buf *bytes.Buffer) (PCRSelection, error) { +func decodeTPMLPCRSelection(buf *bytes.Buffer) ([]PCRSelection, error) { var count uint32 - var sel PCRSelection + var sel []PCRSelection + + // This unpacks buffer which is of type TPMLPCRSelection + // and returns the count of TPMSPCRSelections. if err := tpmutil.UnpackBuf(buf, &count); err != nil { return sel, err } - if count != 1 { - return sel, fmt.Errorf("decoding TPML_PCR_SELECTION list longer than 1 is not supported (got length %d)", count) - } - // See comment in encodeTPMLPCRSelection for details on this format. var ts tpmsPCRSelection - if err := tpmutil.UnpackBuf(buf, &ts.Hash, &ts.Size); err != nil { - return sel, err - } - ts.PCRs = make([]byte, ts.Size) - if _, err := buf.Read(ts.PCRs); err != nil { - return sel, err - } - - sel.Hash = ts.Hash - for i := 0; i < int(ts.Size); i++ { - for j := 0; j < 8; j++ { - set := ts.PCRs[i] & byte(1< 0, nil + case CapabilityAlgs: + var numAlgs uint32 + if err := tpmutil.UnpackBuf(buf, &numAlgs); err != nil { + return nil, false, fmt.Errorf("could not unpack algorithm count: %v", err) } - handles = append(handles, handle) - } - return capReported, handles, nil + var algs []interface{} + for i := 0; i < int(numAlgs); i++ { + var alg AlgorithmDescription + if err := tpmutil.UnpackBuf(buf, &alg); err != nil { + return nil, false, fmt.Errorf("could not unpack algorithm description: %v", err) + } + algs = append(algs, alg) + } + return algs, moreData > 0, nil + case CapabilityTPMProperties: + var numProps uint32 + if err := tpmutil.UnpackBuf(buf, &numProps); err != nil { + return nil, false, fmt.Errorf("could not unpack fixed properties count: %v", err) + } + + var props []interface{} + for i := 0; i < int(numProps); i++ { + var prop TaggedProperty + if err := tpmutil.UnpackBuf(buf, &prop); err != nil { + return nil, false, fmt.Errorf("could not unpack tagged property: %v", err) + } + props = append(props, prop) + } + return props, moreData > 0, nil + + case CapabilityPCRs: + var pcrss []interface{} + pcrs, err := decodeTPMLPCRSelection(buf) + if err != nil { + return nil, false, fmt.Errorf("could not unpack pcr selection: %v", err) + } + for i := 0; i < len(pcrs); i++ { + pcrss = append(pcrss, pcrs[i]) + } + + return pcrss, moreData > 0, nil + + default: + return nil, false, fmt.Errorf("unsupported capability %v", capReported) + } } // GetCapability returns various information about the TPM state. // -// Currently only CapabilityHandles is supported (list active handles). -func GetCapability(rw io.ReadWriter, cap Capability, count uint32, property uint32) ([]tpmutil.Handle, error) { - resp, err := runCommand(rw, tagNoSessions, cmdGetCapability, cap, property, count) +// Currently only CapabilityHandles (list active handles) and CapabilityAlgs +// (list supported algorithms) are supported. CapabilityHandles will return +// a []tpmutil.Handle for vals, CapabilityAlgs will return +// []AlgorithmDescription. +// +// moreData is true if the TPM indicated that more data is available. Follow +// the spec for the capability in question on how to query for more data. +func GetCapability(rw io.ReadWriter, capa Capability, count, property uint32) (vals []interface{}, moreData bool, err error) { + resp, err := runCommand(rw, TagNoSessions, cmdGetCapability, capa, property, count) if err != nil { - return nil, err + return nil, false, err } - _, handles, err := decodeGetCapability(resp) + return decodeGetCapability(resp) +} + +// GetManufacturer returns the manufacturer ID +func GetManufacturer(rw io.ReadWriter) ([]byte, error) { + caps, _, err := GetCapability(rw, CapabilityTPMProperties, 1, uint32(Manufacturer)) if err != nil { return nil, err } - return handles, nil + + prop := caps[0].(TaggedProperty) + return tpmutil.Pack(prop.Value) } -func encodePasswordAuthArea(passwords ...string) ([]byte, error) { - var res []byte - for _, p := range passwords { - // Empty nonce. - var nonce []byte - // continueSession set, all other bits clear. - attributes := byte(1) - buf, err := tpmutil.Pack(HandlePasswordSession, nonce, attributes, []byte(p)) +func encodeAuthArea(sections ...AuthCommand) ([]byte, error) { + var res tpmutil.RawBytes + for _, s := range sections { + buf, err := tpmutil.Pack(s) if err != nil { return nil, err } @@ -255,11 +338,11 @@ func encodePCREvent(pcr tpmutil.Handle, eventData []byte) ([]byte, error) { if err != nil { return nil, err } - auth, err := encodePasswordAuthArea("") + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: EmptyAuth}) if err != nil { return nil, err } - event, err := tpmutil.Pack(eventData) + event, err := tpmutil.Pack(tpmutil.U16Bytes(eventData)) if err != nil { return nil, err } @@ -272,7 +355,7 @@ func PCREvent(rw io.ReadWriter, pcr tpmutil.Handle, eventData []byte) error { if err != nil { return err } - _, err = runCommand(rw, tagSessions, cmdPCREvent, tpmutil.RawBytes(cmd)) + _, err = runCommand(rw, TagSessions, cmdPCREvent, tpmutil.RawBytes(cmd)) return err } @@ -283,36 +366,35 @@ func encodeSensitiveArea(s tpmsSensitiveCreate) ([]byte, error) { return nil, err } // TPM2B_SENSITIVE_CREATE - return tpmutil.Pack(buf) + return tpmutil.Pack(tpmutil.U16Bytes(buf)) } // encodeCreate works for both TPM2_Create and TPM2_CreatePrimary. -func encodeCreate(owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public) ([]byte, error) { - inPublic, err := pub.encode() +func encodeCreate(owner tpmutil.Handle, sel PCRSelection, auth AuthCommand, ownerPassword string, sensitiveData []byte, pub Public) ([]byte, error) { + parent, err := tpmutil.Pack(owner) if err != nil { return nil, err } - return encodeCreateRawTemplate(owner, sel, parentPassword, ownerPassword, inPublic) -} - -func encodeCreateRawTemplate(owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, inPublic []byte) ([]byte, error) { - parent, err := tpmutil.Pack(owner) + encodedAuth, err := encodeAuthArea(auth) if err != nil { return nil, err } - auth, err := encodePasswordAuthArea(parentPassword) + inSensitive, err := encodeSensitiveArea(tpmsSensitiveCreate{ + UserAuth: []byte(ownerPassword), + Data: sensitiveData, + }) if err != nil { return nil, err } - inSensitive, err := encodeSensitiveArea(tpmsSensitiveCreate{UserAuth: []byte(ownerPassword)}) + inPublic, err := pub.Encode() if err != nil { return nil, err } - publicBlob, err := tpmutil.Pack(inPublic) + publicBlob, err := tpmutil.Pack(tpmutil.U16Bytes(inPublic)) if err != nil { return nil, err } - outsideInfo, err := tpmutil.Pack([]byte(nil)) + outsideInfo, err := tpmutil.Pack(tpmutil.U16Bytes(nil)) if err != nil { return nil, err } @@ -322,7 +404,7 @@ func encodeCreateRawTemplate(owner tpmutil.Handle, sel PCRSelection, parentPassw } return concat( parent, - auth, + encodedAuth, inSensitive, publicBlob, outsideInfo, @@ -330,57 +412,67 @@ func encodeCreateRawTemplate(owner tpmutil.Handle, sel PCRSelection, parentPassw ) } -func decodeCreatePrimary(in []byte) (tpmutil.Handle, crypto.PublicKey, error) { - var handle tpmutil.Handle +func decodeCreatePrimary(in []byte) (handle tpmutil.Handle, public, creationData, creationHash tpmutil.U16Bytes, ticket *Ticket, creationName tpmutil.U16Bytes, err error) { var paramSize uint32 buf := bytes.NewBuffer(in) // Handle and auth data. if err := tpmutil.UnpackBuf(buf, &handle, ¶mSize); err != nil { - return 0, nil, err + return 0, nil, nil, nil, nil, nil, fmt.Errorf("decoding handle, paramSize: %v", err) } - var public []byte - if err := tpmutil.UnpackBuf(buf, &public); err != nil { - return 0, nil, fmt.Errorf("decoding TPM2B_PUBLIC: %v", err) + if err := tpmutil.UnpackBuf(buf, &public, &creationData, &creationHash); err != nil { + return 0, nil, nil, nil, nil, nil, fmt.Errorf("decoding public, creationData, creationHash: %v", err) } - pub, err := decodePublic(bytes.NewBuffer(public)) + ticket, err = decodeTicket(buf) if err != nil { - return 0, nil, err + return 0, nil, nil, nil, nil, nil, fmt.Errorf("decoding ticket: %v", err) } - var pubKey crypto.PublicKey - switch pub.Type { - case AlgRSA: - // Endianness of big.Int.Bytes/SetBytes and modulus in the TPM is the same - // (big-endian). - pubKey = &rsa.PublicKey{N: pub.RSAParameters.Modulus, E: int(pub.RSAParameters.Exponent)} - case AlgECC: - curve, ok := toGoCurve[pub.ECCParameters.CurveID] - if !ok { - return 0, nil, fmt.Errorf("can't map TPM EC curve ID 0x%x to Go elliptic.Curve value", pub.ECCParameters.CurveID) - } - pubKey = &ecdsa.PublicKey{ - X: pub.ECCParameters.Point.X, - Y: pub.ECCParameters.Point.Y, - Curve: curve, - } - default: - return 0, nil, fmt.Errorf("unsupported primary key type 0x%x", pub.Type) + + if err := tpmutil.UnpackBuf(buf, &creationName); err != nil { + return 0, nil, nil, nil, nil, nil, fmt.Errorf("decoding creationName: %v", err) } - return handle, pubKey, nil + + if _, err := DecodeCreationData(creationData); err != nil { + return 0, nil, nil, nil, nil, nil, fmt.Errorf("parsing CreationData: %v", err) + } + return handle, public, creationData, creationHash, ticket, creationName, err } // CreatePrimary initializes the primary key in a given hierarchy. -// Second return value is the public part of the generated key. -func CreatePrimary(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public) (tpmutil.Handle, crypto.PublicKey, error) { - cmd, err := encodeCreate(owner, sel, parentPassword, ownerPassword, pub) +// The second return value is the public part of the generated key. +func CreatePrimary(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, p Public) (tpmutil.Handle, crypto.PublicKey, error) { + hnd, public, _, _, _, _, err := CreatePrimaryEx(rw, owner, sel, parentPassword, ownerPassword, p) if err != nil { return 0, nil, err } - resp, err := runCommand(rw, tagSessions, cmdCreatePrimary, tpmutil.RawBytes(cmd)) + + pub, err := DecodePublic(public) if err != nil { - return 0, nil, err + return 0, nil, fmt.Errorf("parsing public: %v", err) + } + + pubKey, err := pub.Key() + if err != nil { + return 0, nil, fmt.Errorf("extracting cryto.PublicKey from Public part of primary key: %v", err) + } + + return hnd, pubKey, err +} + +// CreatePrimaryEx initializes the primary key in a given hierarchy. +// This function differs from CreatePrimary in that all response elements +// are returned, and they are returned in relatively raw form. +func CreatePrimaryEx(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public) (keyHandle tpmutil.Handle, public, creationData, creationHash []byte, ticket *Ticket, creationName []byte, err error) { + auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} + cmd, err := encodeCreate(owner, sel, auth, ownerPassword, nil /*inSensitive*/, pub) + if err != nil { + return 0, nil, nil, nil, nil, nil, err + } + resp, err := runCommand(rw, TagSessions, cmdCreatePrimary, tpmutil.RawBytes(cmd)) + if err != nil { + return 0, nil, nil, nil, nil, nil, err } return decodeCreatePrimary(resp) @@ -390,28 +482,23 @@ func CreatePrimary(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, par // (TPMT_PUBLIC) provided pre-encoded. This is commonly used with key templates // stored in NV RAM. func CreatePrimaryRawTemplate(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, public []byte) (tpmutil.Handle, crypto.PublicKey, error) { - cmd, err := encodeCreateRawTemplate(owner, sel, parentPassword, ownerPassword, public) + pub, err := DecodePublic(public) if err != nil { - return 0, nil, err + return 0, nil, fmt.Errorf("parsing input template: %v", err) } - resp, err := runCommand(rw, tagSessions, cmdCreatePrimary, tpmutil.RawBytes(cmd)) - if err != nil { - return 0, nil, err - } - - return decodeCreatePrimary(resp) + return CreatePrimary(rw, owner, sel, parentPassword, ownerPassword, pub) } func decodeReadPublic(in []byte) (Public, []byte, []byte, error) { var resp struct { - Public []byte - Name []byte - QualifiedName []byte + Public tpmutil.U16Bytes + Name tpmutil.U16Bytes + QualifiedName tpmutil.U16Bytes } if _, err := tpmutil.Unpack(in, &resp); err != nil { return Public{}, nil, nil, err } - pub, err := decodePublic(bytes.NewBuffer(resp.Public)) + pub, err := DecodePublic(resp.Public) if err != nil { return Public{}, nil, nil, err } @@ -421,7 +508,7 @@ func decodeReadPublic(in []byte) (Public, []byte, []byte, error) { // ReadPublic reads the public part of the object under handle. // Returns the public data, name and qualified name. func ReadPublic(rw io.ReadWriter, handle tpmutil.Handle) (Public, []byte, []byte, error) { - resp, err := runCommand(rw, tagNoSessions, cmdReadPublic, handle) + resp, err := runCommand(rw, TagNoSessions, cmdReadPublic, handle) if err != nil { return Public{}, nil, nil, err } @@ -429,39 +516,74 @@ func ReadPublic(rw io.ReadWriter, handle tpmutil.Handle) (Public, []byte, []byte return decodeReadPublic(resp) } -func decodeCreateKey(in []byte) ([]byte, []byte, error) { - var resp struct { - Handle tpmutil.Handle - Private []byte - Public []byte +func decodeCreate(in []byte) (private, public, creationData, creationHash tpmutil.U16Bytes, creationTicket Ticket, err error) { + buf := bytes.NewBuffer(in) + var paramSize uint32 + if err := tpmutil.UnpackBuf(buf, ¶mSize, &private, &public, &creationData, &creationHash); err != nil { + return nil, nil, nil, nil, Ticket{}, fmt.Errorf("decoding Handle, Private, Public, CreationData, CreationHash: %v", err) + } + cr, err := decodeTicket(buf) + if err != nil { + return nil, nil, nil, nil, Ticket{}, fmt.Errorf("decoding CreationTicket: %v", err) } + creationTicket = *cr + if _, err := DecodeCreationData(creationData); err != nil { + return nil, nil, nil, nil, Ticket{}, fmt.Errorf("decoding CreationData: %v", err) + } + return private, public, creationData, creationHash, creationTicket, nil +} - if _, err := tpmutil.Unpack(in, &resp); err != nil { - return nil, nil, err +func create(rw io.ReadWriter, parentHandle tpmutil.Handle, auth AuthCommand, objectPassword string, sensitiveData []byte, pub Public, pcrSelection PCRSelection) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { + cmd, err := encodeCreate(parentHandle, pcrSelection, auth, objectPassword, sensitiveData, pub) + if err != nil { + return nil, nil, nil, nil, Ticket{}, err + } + resp, err := runCommand(rw, TagSessions, cmdCreate, tpmutil.RawBytes(cmd)) + if err != nil { + return nil, nil, nil, nil, Ticket{}, err } - return resp.Private, resp.Public, nil + return decodeCreate(resp) } // CreateKey creates a new key pair under the owner handle. -// Returns private key and public key blobs. -func CreateKey(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public) ([]byte, []byte, error) { - cmd, err := encodeCreate(owner, sel, parentPassword, ownerPassword, pub) - if err != nil { - return nil, nil, err +// Returns private key and public key blobs as well as the +// creation data, a hash of said data and the creation ticket. +func CreateKey(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { + auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} + return create(rw, owner, auth, ownerPassword, nil /*inSensitive*/, pub, sel) +} + +// CreateKeyUsingAuth creates a new key pair under the owner handle using the +// provided AuthCommand. Returns private key and public key blobs as well as +// the creation data, a hash of said data, and the creation ticket. +func CreateKeyUsingAuth(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, auth AuthCommand, ownerPassword string, pub Public) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { + return create(rw, owner, auth, ownerPassword, nil /*inSensitive*/, pub, sel) +} + +// Seal creates a data blob object that seals the sensitive data under a parent and with a +// password and auth policy. Access to the parent must be available with a simple password. +// Returns private and public portions of the created object. +func Seal(rw io.ReadWriter, parentHandle tpmutil.Handle, parentPassword, objectPassword string, objectAuthPolicy []byte, sensitiveData []byte) ([]byte, []byte, error) { + inPublic := Public{ + Type: AlgKeyedHash, + NameAlg: AlgSHA256, + Attributes: FlagFixedTPM | FlagFixedParent, + AuthPolicy: objectAuthPolicy, } - resp, err := runCommand(rw, tagSessions, cmdCreate, tpmutil.RawBytes(cmd)) + auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} + private, public, _, _, _, err := create(rw, parentHandle, auth, objectPassword, sensitiveData, inPublic, PCRSelection{}) if err != nil { return nil, nil, err } - return decodeCreateKey(resp) + return private, public, nil } -func encodeLoad(parentHandle tpmutil.Handle, parentAuth string, publicBlob, privateBlob []byte) ([]byte, error) { +func encodeLoad(parentHandle tpmutil.Handle, auth AuthCommand, publicBlob, privateBlob tpmutil.U16Bytes) ([]byte, error) { ah, err := tpmutil.Pack(parentHandle) if err != nil { return nil, err } - auth, err := encodePasswordAuthArea(parentAuth) + encodedAuth, err := encodeAuthArea(auth) if err != nil { return nil, err } @@ -469,13 +591,13 @@ func encodeLoad(parentHandle tpmutil.Handle, parentAuth string, publicBlob, priv if err != nil { return nil, err } - return concat(ah, auth, params) + return concat(ah, encodedAuth, params) } func decodeLoad(in []byte) (tpmutil.Handle, []byte, error) { var handle tpmutil.Handle var paramSize uint32 - var name []byte + var name tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, &handle, ¶mSize, &name); err != nil { return 0, nil, err @@ -486,11 +608,18 @@ func decodeLoad(in []byte) (tpmutil.Handle, []byte, error) { // Load loads public/private blobs into an object in the TPM. // Returns loaded object handle and its name. func Load(rw io.ReadWriter, parentHandle tpmutil.Handle, parentAuth string, publicBlob, privateBlob []byte) (tpmutil.Handle, []byte, error) { - cmd, err := encodeLoad(parentHandle, parentAuth, publicBlob, privateBlob) + auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentAuth)} + return LoadUsingAuth(rw, parentHandle, auth, publicBlob, privateBlob) +} + +// LoadUsingAuth loads public/private blobs into an object in the TPM using the +// provided AuthCommand. Returns loaded object handle and its name. +func LoadUsingAuth(rw io.ReadWriter, parentHandle tpmutil.Handle, auth AuthCommand, publicBlob, privateBlob []byte) (tpmutil.Handle, []byte, error) { + cmd, err := encodeLoad(parentHandle, auth, publicBlob, privateBlob) if err != nil { return 0, nil, err } - resp, err := runCommand(rw, tagSessions, cmdLoad, tpmutil.RawBytes(cmd)) + resp, err := runCommand(rw, TagSessions, cmdLoad, tpmutil.RawBytes(cmd)) if err != nil { return 0, nil, err } @@ -498,21 +627,21 @@ func Load(rw io.ReadWriter, parentHandle tpmutil.Handle, parentAuth string, publ } func encodeLoadExternal(pub Public, private Private, hierarchy tpmutil.Handle) ([]byte, error) { - privateBlob, err := private.encode() + privateBlob, err := private.Encode() if err != nil { return nil, err } - publicBlob, err := pub.encode() + publicBlob, err := pub.Encode() if err != nil { return nil, err } - return tpmutil.Pack(privateBlob, publicBlob, hierarchy) + return tpmutil.Pack(tpmutil.U16Bytes(privateBlob), tpmutil.U16Bytes(publicBlob), hierarchy) } func decodeLoadExternal(in []byte) (tpmutil.Handle, []byte, error) { var handle tpmutil.Handle - var name []byte + var name tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, &handle, &name); err != nil { return 0, nil, err @@ -527,7 +656,7 @@ func LoadExternal(rw io.ReadWriter, pub Public, private Private, hierarchy tpmut if err != nil { return 0, nil, err } - resp, err := runCommand(rw, tagNoSessions, cmdLoadExternal, tpmutil.RawBytes(cmd)) + resp, err := runCommand(rw, TagNoSessions, cmdLoadExternal, tpmutil.RawBytes(cmd)) if err != nil { return 0, nil, err } @@ -540,11 +669,62 @@ func LoadExternal(rw io.ReadWriter, pub Public, private Private, hierarchy tpmut // PolicyPassword sets password authorization requirement on the object. func PolicyPassword(rw io.ReadWriter, handle tpmutil.Handle) error { - _, err := runCommand(rw, tagNoSessions, cmdPolicyPassword, handle) + _, err := runCommand(rw, TagNoSessions, cmdPolicyPassword, handle) return err } -func encodePolicyPCR(session tpmutil.Handle, expectedDigest []byte, sel PCRSelection) ([]byte, error) { +func encodePolicySecret(entityHandle tpmutil.Handle, entityAuth AuthCommand, policyHandle tpmutil.Handle, policyNonce, cpHash, policyRef tpmutil.U16Bytes, expiry int32) ([]byte, error) { + auth, err := encodeAuthArea(entityAuth) + if err != nil { + return nil, err + } + handles, err := tpmutil.Pack(entityHandle, policyHandle) + if err != nil { + return nil, err + } + params, err := tpmutil.Pack(policyNonce, cpHash, policyRef, expiry) + if err != nil { + return nil, err + } + return concat(handles, auth, params) +} + +func decodePolicySecret(in []byte) (*Ticket, error) { + buf := bytes.NewBuffer(in) + + var paramSize uint32 + var timeout tpmutil.U16Bytes + if err := tpmutil.UnpackBuf(buf, ¶mSize, &timeout); err != nil { + return nil, fmt.Errorf("decoding timeout: %v", err) + } + + ticket, err := decodeTicket(buf) + if err != nil { + return nil, fmt.Errorf("decoding ticket: %v", err) + } + return ticket, nil +} + +// PolicySecret sets a secret authorization requirement on the provided entity. +// If expiry is non-zero, the authorization is valid for expiry seconds. +func PolicySecret(rw io.ReadWriter, entityHandle tpmutil.Handle, entityAuth AuthCommand, policyHandle tpmutil.Handle, policyNonce, cpHash, policyRef []byte, expiry int32) (*Ticket, error) { + cmd, err := encodePolicySecret(entityHandle, entityAuth, policyHandle, policyNonce, cpHash, policyRef, expiry) + if err != nil { + return nil, err + } + resp, err := runCommand(rw, TagSessions, CmdPolicySecret, tpmutil.RawBytes(cmd)) + if err != nil { + return nil, err + } + + // Tickets are only provided if expiry is set. + if expiry != 0 { + return decodePolicySecret(resp) + } + return nil, nil +} + +func encodePolicyPCR(session tpmutil.Handle, expectedDigest tpmutil.U16Bytes, sel PCRSelection) ([]byte, error) { params, err := tpmutil.Pack(session, expectedDigest) if err != nil { return nil, err @@ -562,23 +742,23 @@ func PolicyPCR(rw io.ReadWriter, session tpmutil.Handle, expectedDigest []byte, if err != nil { return err } - _, err = runCommand(rw, tagNoSessions, cmdPolicyPCR, tpmutil.RawBytes(cmd)) + _, err = runCommand(rw, TagNoSessions, CmdPolicyPCR, tpmutil.RawBytes(cmd)) return err } // PolicyGetDigest returns the current policyDigest of the session. func PolicyGetDigest(rw io.ReadWriter, handle tpmutil.Handle) ([]byte, error) { - resp, err := runCommand(rw, tagNoSessions, cmdPolicyGetDigest, handle) + resp, err := runCommand(rw, TagNoSessions, cmdPolicyGetDigest, handle) if err != nil { return nil, err } - var digest []byte + var digest tpmutil.U16Bytes _, err = tpmutil.Unpack(resp, &digest) return digest, err } -func encodeStartAuthSession(tpmKey, bindKey tpmutil.Handle, nonceCaller, secret []byte, se SessionType, sym, hashAlg Algorithm) ([]byte, error) { +func encodeStartAuthSession(tpmKey, bindKey tpmutil.Handle, nonceCaller, secret tpmutil.U16Bytes, se SessionType, sym, hashAlg Algorithm) ([]byte, error) { ha, err := tpmutil.Pack(tpmKey, bindKey) if err != nil { return nil, err @@ -592,7 +772,7 @@ func encodeStartAuthSession(tpmKey, bindKey tpmutil.Handle, nonceCaller, secret func decodeStartAuthSession(in []byte) (tpmutil.Handle, []byte, error) { var handle tpmutil.Handle - var nonce []byte + var nonce tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, &handle, &nonce); err != nil { return 0, nil, err } @@ -606,19 +786,19 @@ func StartAuthSession(rw io.ReadWriter, tpmKey, bindKey tpmutil.Handle, nonceCal if err != nil { return 0, nil, err } - resp, err := runCommand(rw, tagNoSessions, cmdStartAuthSession, tpmutil.RawBytes(cmd)) + resp, err := runCommand(rw, TagNoSessions, cmdStartAuthSession, tpmutil.RawBytes(cmd)) if err != nil { return 0, nil, err } return decodeStartAuthSession(resp) } -func encodeUnseal(itemHandle tpmutil.Handle, password string) ([]byte, error) { +func encodeUnseal(sessionHandle, itemHandle tpmutil.Handle, password string) ([]byte, error) { ha, err := tpmutil.Pack(itemHandle) if err != nil { return nil, err } - auth, err := encodePasswordAuthArea(password) + auth, err := encodeAuthArea(AuthCommand{Session: sessionHandle, Attributes: AttrContinueSession, Auth: []byte(password)}) if err != nil { return nil, err } @@ -627,7 +807,7 @@ func encodeUnseal(itemHandle tpmutil.Handle, password string) ([]byte, error) { func decodeUnseal(in []byte) ([]byte, error) { var paramSize uint32 - var unsealed []byte + var unsealed tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, ¶mSize, &unsealed); err != nil { return nil, err @@ -637,23 +817,28 @@ func decodeUnseal(in []byte) ([]byte, error) { // Unseal returns the data for a loaded sealed object. func Unseal(rw io.ReadWriter, itemHandle tpmutil.Handle, password string) ([]byte, error) { - cmd, err := encodeUnseal(itemHandle, password) + return UnsealWithSession(rw, HandlePasswordSession, itemHandle, password) +} + +// UnsealWithSession returns the data for a loaded sealed object. +func UnsealWithSession(rw io.ReadWriter, sessionHandle, itemHandle tpmutil.Handle, password string) ([]byte, error) { + cmd, err := encodeUnseal(sessionHandle, itemHandle, password) if err != nil { return nil, err } - resp, err := runCommand(rw, tagSessions, cmdUnseal, tpmutil.RawBytes(cmd)) + resp, err := runCommand(rw, TagSessions, cmdUnseal, tpmutil.RawBytes(cmd)) if err != nil { return nil, err } return decodeUnseal(resp) } -func encodeQuote(signingHandle tpmutil.Handle, parentPassword, ownerPassword string, toQuote []byte, sel PCRSelection, sigAlg Algorithm) ([]byte, error) { +func encodeQuote(signingHandle tpmutil.Handle, parentPassword, ownerPassword string, toQuote tpmutil.U16Bytes, sel PCRSelection, sigAlg Algorithm) ([]byte, error) { ha, err := tpmutil.Pack(signingHandle) if err != nil { return nil, err } - auth, err := encodePasswordAuthArea(parentPassword) + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)}) if err != nil { return nil, err } @@ -671,11 +856,11 @@ func encodeQuote(signingHandle tpmutil.Handle, parentPassword, ownerPassword str func decodeQuote(in []byte) ([]byte, *Signature, error) { buf := bytes.NewBuffer(in) var paramSize uint32 - var attest []byte + var attest tpmutil.U16Bytes if err := tpmutil.UnpackBuf(buf, ¶mSize, &attest); err != nil { return nil, nil, err } - sig, err := decodeSignature(buf) + sig, err := DecodeSignature(buf) return attest, sig, err } @@ -690,19 +875,19 @@ func Quote(rw io.ReadWriter, signingHandle tpmutil.Handle, parentPassword, owner if err != nil { return nil, nil, err } - resp, err := runCommand(rw, tagSessions, cmdQuote, tpmutil.RawBytes(cmd)) + resp, err := runCommand(rw, TagSessions, cmdQuote, tpmutil.RawBytes(cmd)) if err != nil { return nil, nil, err } return decodeQuote(resp) } -func encodeActivateCredential(activeHandle tpmutil.Handle, keyHandle tpmutil.Handle, activePassword, protectorPassword string, credBlob, secret []byte) ([]byte, error) { +func encodeActivateCredential(auth []AuthCommand, activeHandle tpmutil.Handle, keyHandle tpmutil.Handle, credBlob, secret tpmutil.U16Bytes) ([]byte, error) { ha, err := tpmutil.Pack(activeHandle, keyHandle) if err != nil { return nil, err } - auth, err := encodePasswordAuthArea(activePassword, protectorPassword) + a, err := encodeAuthArea(auth...) if err != nil { return nil, err } @@ -710,12 +895,12 @@ func encodeActivateCredential(activeHandle tpmutil.Handle, keyHandle tpmutil.Han if err != nil { return nil, err } - return concat(ha, auth, params) + return concat(ha, a, params) } func decodeActivateCredential(in []byte) ([]byte, error) { var paramSize uint32 - var certInfo []byte + var certInfo tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, ¶mSize, &certInfo); err != nil { return nil, err @@ -726,18 +911,32 @@ func decodeActivateCredential(in []byte) ([]byte, error) { // ActivateCredential associates an object with a credential. // Returns decrypted certificate information. func ActivateCredential(rw io.ReadWriter, activeHandle, keyHandle tpmutil.Handle, activePassword, protectorPassword string, credBlob, secret []byte) ([]byte, error) { - cmd, err := encodeActivateCredential(activeHandle, keyHandle, activePassword, protectorPassword, credBlob, secret) + return ActivateCredentialUsingAuth(rw, []AuthCommand{ + {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(activePassword)}, + {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(protectorPassword)}, + }, activeHandle, keyHandle, credBlob, secret) +} + +// ActivateCredentialUsingAuth associates an object with a credential, using the +// given set of authorizations. Two authorization must be provided. +// Returns decrypted certificate information. +func ActivateCredentialUsingAuth(rw io.ReadWriter, auth []AuthCommand, activeHandle, keyHandle tpmutil.Handle, credBlob, secret []byte) ([]byte, error) { + if len(auth) != 2 { + return nil, fmt.Errorf("len(auth) = %d, want 2", len(auth)) + } + + cmd, err := encodeActivateCredential(auth, activeHandle, keyHandle, credBlob, secret) if err != nil { return nil, err } - resp, err := runCommand(rw, tagSessions, cmdActivateCredential, tpmutil.RawBytes(cmd)) + resp, err := runCommand(rw, TagSessions, cmdActivateCredential, tpmutil.RawBytes(cmd)) if err != nil { return nil, err } return decodeActivateCredential(resp) } -func encodeMakeCredential(protectorHandle tpmutil.Handle, credential, activeName []byte) ([]byte, error) { +func encodeMakeCredential(protectorHandle tpmutil.Handle, credential, activeName tpmutil.U16Bytes) ([]byte, error) { ha, err := tpmutil.Pack(protectorHandle) if err != nil { return nil, err @@ -750,8 +949,7 @@ func encodeMakeCredential(protectorHandle tpmutil.Handle, credential, activeName } func decodeMakeCredential(in []byte) ([]byte, []byte, error) { - var credBlob []byte - var encryptedSecret []byte + var credBlob, encryptedSecret tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, &credBlob, &encryptedSecret); err != nil { return nil, nil, err @@ -766,7 +964,7 @@ func MakeCredential(rw io.ReadWriter, protectorHandle tpmutil.Handle, credential if err != nil { return nil, nil, err } - resp, err := runCommand(rw, tagNoSessions, cmdMakeCredential, tpmutil.RawBytes(cmd)) + resp, err := runCommand(rw, TagNoSessions, cmdMakeCredential, tpmutil.RawBytes(cmd)) if err != nil { return nil, nil, err } @@ -778,7 +976,7 @@ func encodeEvictControl(ownerAuth string, owner, objectHandle, persistentHandle if err != nil { return nil, err } - auth, err := encodePasswordAuthArea(ownerAuth) + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(ownerAuth)}) if err != nil { return nil, err } @@ -795,7 +993,7 @@ func EvictControl(rw io.ReadWriter, ownerAuth string, owner, objectHandle, persi if err != nil { return err } - _, err = runCommand(rw, tagSessions, cmdEvictControl, tpmutil.RawBytes(cmd)) + _, err = runCommand(rw, TagSessions, cmdEvictControl, tpmutil.RawBytes(cmd)) return err } @@ -803,12 +1001,12 @@ func EvictControl(rw io.ReadWriter, ownerAuth string, owner, objectHandle, persi // context for storage outside of the TPM. The handle references context to // store. func ContextSave(rw io.ReadWriter, handle tpmutil.Handle) ([]byte, error) { - return runCommand(rw, tagNoSessions, cmdContextSave, handle) + return runCommand(rw, TagNoSessions, cmdContextSave, handle) } // ContextLoad reloads context data created by ContextSave. func ContextLoad(rw io.ReadWriter, saveArea []byte) (tpmutil.Handle, error) { - resp, err := runCommand(rw, tagNoSessions, cmdContextLoad, tpmutil.RawBytes(saveArea)) + resp, err := runCommand(rw, TagNoSessions, cmdContextLoad, tpmutil.RawBytes(saveArea)) if err != nil { return 0, err } @@ -818,7 +1016,7 @@ func ContextLoad(rw io.ReadWriter, saveArea []byte) (tpmutil.Handle, error) { } func encodeIncrementNV(handle tpmutil.Handle, authString string) ([]byte, error) { - auth, err := encodePasswordAuthArea(authString) + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(authString)}) if err != nil { return nil, err } @@ -835,12 +1033,12 @@ func NVIncrement(rw io.ReadWriter, handle tpmutil.Handle, authString string) err if err != nil { return err } - _, err = runCommand(rw, tagSessions, cmdIncrementNVCounter, tpmutil.RawBytes(cmd)) + _, err = runCommand(rw, TagSessions, cmdIncrementNVCounter, tpmutil.RawBytes(cmd)) return err } func encodeUndefineSpace(ownerAuth string, owner, index tpmutil.Handle) ([]byte, error) { - auth, err := encodePasswordAuthArea(ownerAuth) + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(ownerAuth)}) if err != nil { return nil, err } @@ -857,16 +1055,16 @@ func NVUndefineSpace(rw io.ReadWriter, ownerAuth string, owner, index tpmutil.Ha if err != nil { return err } - _, err = runCommand(rw, tagSessions, cmdUndefineSpace, tpmutil.RawBytes(cmd)) + _, err = runCommand(rw, TagSessions, cmdUndefineSpace, tpmutil.RawBytes(cmd)) return err } -func encodeDefineSpace(owner, handle tpmutil.Handle, ownerAuth, authVal string, attributes uint32, policy []byte, dataSize uint16) ([]byte, error) { +func encodeDefineSpace(owner, handle tpmutil.Handle, ownerAuth, authVal string, attributes NVAttr, policy tpmutil.U16Bytes, dataSize uint16) ([]byte, error) { ha, err := tpmutil.Pack(owner) if err != nil { return nil, err } - auth, err := encodePasswordAuthArea(ownerAuth) + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(ownerAuth)}) if err != nil { return nil, err } @@ -874,7 +1072,7 @@ func encodeDefineSpace(owner, handle tpmutil.Handle, ownerAuth, authVal string, if err != nil { return nil, err } - params, err := tpmutil.Pack([]byte(authVal), publicInfo) + params, err := tpmutil.Pack(tpmutil.U16Bytes(authVal), tpmutil.U16Bytes(publicInfo)) if err != nil { return nil, err } @@ -882,18 +1080,44 @@ func encodeDefineSpace(owner, handle tpmutil.Handle, ownerAuth, authVal string, } // NVDefineSpace creates an index in TPM's NV storage. -func NVDefineSpace(rw io.ReadWriter, owner, handle tpmutil.Handle, ownerAuth, authString string, policy []byte, attributes uint32, dataSize uint16) error { +func NVDefineSpace(rw io.ReadWriter, owner, handle tpmutil.Handle, ownerAuth, authString string, policy []byte, attributes NVAttr, dataSize uint16) error { cmd, err := encodeDefineSpace(owner, handle, ownerAuth, authString, attributes, policy, dataSize) if err != nil { return err } - _, err = runCommand(rw, tagSessions, cmdDefineSpace, tpmutil.RawBytes(cmd)) + _, err = runCommand(rw, TagSessions, cmdDefineSpace, tpmutil.RawBytes(cmd)) + return err +} + +func encodeWriteNV(owner, handle tpmutil.Handle, authString string, data tpmutil.U16Bytes, offset uint16) ([]byte, error) { + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(authString)}) + if err != nil { + return nil, err + } + out, err := tpmutil.Pack(owner, handle) + if err != nil { + return nil, err + } + buf, err := tpmutil.Pack(data, offset) + if err != nil { + return nil, err + } + return concat(out, auth, buf) +} + +// NVWrite writes data into the TPM's NV storage. +func NVWrite(rw io.ReadWriter, owner, handle tpmutil.Handle, authString string, data tpmutil.U16Bytes, offset uint16) error { + cmd, err := encodeWriteNV(owner, handle, authString, data, offset) + if err != nil { + return err + } + _, err = runCommand(rw, TagSessions, cmdWriteNV, tpmutil.RawBytes(cmd)) return err } func decodeNVReadPublic(in []byte) (NVPublic, error) { var pub NVPublic - var buf []byte + var buf tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, &buf); err != nil { return pub, err } @@ -901,63 +1125,108 @@ func decodeNVReadPublic(in []byte) (NVPublic, error) { return pub, err } +// NVReadPublic reads the public data of an NV index. +func NVReadPublic(rw io.ReadWriter, index tpmutil.Handle) (NVPublic, error) { + // Read public area to determine data size. + resp, err := runCommand(rw, TagNoSessions, cmdReadPublicNV, index) + if err != nil { + return NVPublic{}, err + } + return decodeNVReadPublic(resp) +} + func decodeNVRead(in []byte) ([]byte, error) { - var sessionAttributes uint32 - var data []byte - if _, err := tpmutil.Unpack(in, &sessionAttributes, &data); err != nil { + var paramSize uint32 + var data tpmutil.U16Bytes + if _, err := tpmutil.Unpack(in, ¶mSize, &data); err != nil { return nil, err } return data, nil } -func encodeNVRead(handle tpmutil.Handle, authString string, offset, dataSize uint16) ([]byte, error) { - ha, err := tpmutil.Pack(handle, handle) +func encodeNVRead(nvIndex, authHandle tpmutil.Handle, password string, offset, dataSize uint16) ([]byte, error) { + handles, err := tpmutil.Pack(authHandle, nvIndex) if err != nil { return nil, err } - auth, err := encodePasswordAuthArea(authString) + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(password)}) if err != nil { return nil, err } + params, err := tpmutil.Pack(dataSize, offset) if err != nil { return nil, err } - return concat(ha, auth, params) + + return concat(handles, auth, params) } -// NVRead reads a full data blob from an NV index. +// NVRead reads a full data blob from an NV index. This function is +// deprecated; use NVReadEx instead. func NVRead(rw io.ReadWriter, index tpmutil.Handle) ([]byte, error) { - // Read public area to determine data size. - resp, err := runCommand(rw, tagNoSessions, cmdReadPublicNV, index) - if err != nil { - return nil, fmt.Errorf("running NV_ReadPublic command: %v", err) + return NVReadEx(rw, index, index, "", 0) +} + +// NVReadEx reads a full data blob from an NV index, using the given +// authorization handle. NVRead commands are done in blocks of blockSize. +// If blockSize is 0, the TPM is queried for TPM_PT_NV_BUFFER_MAX, and that +// value is used. +func NVReadEx(rw io.ReadWriter, index, authHandle tpmutil.Handle, password string, blockSize int) ([]byte, error) { + if blockSize == 0 { + readBuff, _, err := GetCapability(rw, CapabilityTPMProperties, 1, uint32(NVMaxBufferSize)) + if err != nil { + return nil, fmt.Errorf("GetCapability for TPM_PT_NV_BUFFER_MAX failed: %v", err) + } + if len(readBuff) != 1 { + return nil, fmt.Errorf("could not determine NVRAM read/write buffer size") + } + rb, ok := readBuff[0].(TaggedProperty) + if !ok { + return nil, fmt.Errorf("GetCapability returned unexpected type: %T, expected TaggedProperty", readBuff[0]) + } + blockSize = int(rb.Value) } - pub, err := decodeNVReadPublic(resp) + + // Read public area to determine data size. + pub, err := NVReadPublic(rw, index) if err != nil { return nil, fmt.Errorf("decoding NV_ReadPublic response: %v", err) } - // Read pub.DataSize of actual data. - cmd, err := encodeNVRead(index, "", 0, pub.DataSize) - if err != nil { - return nil, fmt.Errorf("building NV_Read command: %v", err) - } - resp, err = runCommand(rw, tagSessions, cmdReadNV, tpmutil.RawBytes(cmd)) - if err != nil { - return nil, fmt.Errorf("running NV_Read command: %v", err) + // Read the NVRAM area in blocks. + outBuff := make([]byte, 0, int(pub.DataSize)) + for len(outBuff) < int(pub.DataSize) { + readSize := blockSize + if readSize > (int(pub.DataSize) - len(outBuff)) { + readSize = int(pub.DataSize) - len(outBuff) + } + + cmd, err := encodeNVRead(index, authHandle, password, uint16(len(outBuff)), uint16(readSize)) + if err != nil { + return nil, fmt.Errorf("building NV_Read command: %v", err) + } + resp, err := runCommand(rw, TagSessions, cmdReadNV, tpmutil.RawBytes(cmd)) + if err != nil { + return nil, fmt.Errorf("running NV_Read command (cursor=%d,size=%d): %v", len(outBuff), readSize, err) + } + data, err := decodeNVRead(resp) + if err != nil { + return nil, fmt.Errorf("decoding NV_Read command: %v", err) + } + outBuff = append(outBuff, data...) } - return decodeNVRead(resp) + return outBuff, nil } // Hash computes a hash of data in buf using the TPM. -func Hash(rw io.ReadWriter, alg Algorithm, buf []byte) ([]byte, error) { - resp, err := runCommand(rw, tagNoSessions, cmdHash, buf, alg, HandleNull) +func Hash(rw io.ReadWriter, alg Algorithm, buf tpmutil.U16Bytes) ([]byte, error) { + resp, err := runCommand(rw, TagNoSessions, cmdHash, buf, alg, HandleNull) if err != nil { return nil, err } - var digest []byte + var digest tpmutil.U16Bytes if _, err = tpmutil.Unpack(resp, &digest); err != nil { return nil, err } @@ -966,22 +1235,22 @@ func Hash(rw io.ReadWriter, alg Algorithm, buf []byte) ([]byte, error) { // Startup initializes a TPM (usually done by the OS). func Startup(rw io.ReadWriter, typ StartupType) error { - _, err := runCommand(rw, tagNoSessions, cmdStartup, typ) + _, err := runCommand(rw, TagNoSessions, cmdStartup, typ) return err } // Shutdown shuts down a TPM (usually done by the OS). func Shutdown(rw io.ReadWriter, typ StartupType) error { - _, err := runCommand(rw, tagNoSessions, cmdShutdown, typ) + _, err := runCommand(rw, TagNoSessions, cmdShutdown, typ) return err } -func encodeSign(key tpmutil.Handle, password string, digest []byte, sigScheme *SigScheme) ([]byte, error) { +func encodeSign(key tpmutil.Handle, password string, digest tpmutil.U16Bytes, sigScheme *SigScheme) ([]byte, error) { ha, err := tpmutil.Pack(key) if err != nil { return nil, err } - auth, err := encodePasswordAuthArea(password) + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(password)}) if err != nil { return nil, err } @@ -993,11 +1262,11 @@ func encodeSign(key tpmutil.Handle, password string, digest []byte, sigScheme *S if err != nil { return nil, err } - hc, err := tpmutil.Pack(tagHashCheck) + hc, err := tpmutil.Pack(TagHashCheck) if err != nil { return nil, err } - params, err := tpmutil.Pack(HandleNull, []byte(nil)) + params, err := tpmutil.Pack(HandleNull, tpmutil.U16Bytes(nil)) if err != nil { return nil, err } @@ -1011,7 +1280,7 @@ func decodeSign(buf []byte) (*Signature, error) { if err := tpmutil.UnpackBuf(in, ¶mSize); err != nil { return nil, err } - return decodeSignature(in) + return DecodeSignature(in) } // Sign computes a signature for digest using a given loaded key. Signature @@ -1021,20 +1290,20 @@ func Sign(rw io.ReadWriter, key tpmutil.Handle, password string, digest []byte, if err != nil { return nil, err } - resp, err := runCommand(rw, tagSessions, cmdSign, tpmutil.RawBytes(cmd)) + resp, err := runCommand(rw, TagSessions, cmdSign, tpmutil.RawBytes(cmd)) if err != nil { return nil, err } return decodeSign(resp) } -func encodeCertify(parentAuth, ownerAuth string, object, signer tpmutil.Handle, qualifyingData []byte) ([]byte, error) { +func encodeCertify(parentAuth, ownerAuth string, object, signer tpmutil.Handle, qualifyingData tpmutil.U16Bytes) ([]byte, error) { ha, err := tpmutil.Pack(object, signer) if err != nil { return nil, err } - auth, err := encodePasswordAuthArea(parentAuth, ownerAuth) + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentAuth)}, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(ownerAuth)}) if err != nil { return nil, err } @@ -1050,7 +1319,7 @@ func encodeCertify(parentAuth, ownerAuth string, object, signer tpmutil.Handle, func decodeCertify(resp []byte) ([]byte, []byte, error) { var paramSize uint32 - var attest, signature []byte + var attest, signature tpmutil.U16Bytes var sigAlg, hashAlg Algorithm if _, err := tpmutil.Unpack(resp, ¶mSize, &attest, &sigAlg, &hashAlg, &signature); err != nil { return nil, nil, err @@ -1066,7 +1335,41 @@ func Certify(rw io.ReadWriter, parentAuth, ownerAuth string, object, signer tpmu if err != nil { return nil, nil, err } - resp, err := runCommand(rw, tagSessions, cmdCertify, tpmutil.RawBytes(cmd)) + resp, err := runCommand(rw, TagSessions, cmdCertify, tpmutil.RawBytes(cmd)) + if err != nil { + return nil, nil, err + } + return decodeCertify(resp) +} + +func encodeCertifyCreation(objectAuth string, object, signer tpmutil.Handle, qualifyingData, creationHash tpmutil.U16Bytes, scheme SigScheme, ticket *Ticket) ([]byte, error) { + handles, err := tpmutil.Pack(signer, object) + if err != nil { + return nil, err + } + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(objectAuth)}) + if err != nil { + return nil, err + } + s, err := scheme.encode() + if err != nil { + return nil, err + } + params, err := tpmutil.Pack(qualifyingData, creationHash, tpmutil.RawBytes(s), ticket) + if err != nil { + return nil, err + } + return concat(handles, auth, params) +} + +// CertifyCreation generates a signature of a newly-created & +// loaded TPM object, using signer as the signing key. +func CertifyCreation(rw io.ReadWriter, objectAuth string, object, signer tpmutil.Handle, qualifyingData, creationHash []byte, sigScheme SigScheme, creationTicket *Ticket) (attestation, signature []byte, err error) { + cmd, err := encodeCertifyCreation(objectAuth, object, signer, qualifyingData, creationHash, sigScheme, creationTicket) + if err != nil { + return nil, nil, err + } + resp, err := runCommand(rw, TagSessions, cmdCertifyCreation, tpmutil.RawBytes(cmd)) if err != nil { return nil, nil, err } @@ -1079,9 +1382,9 @@ func runCommand(rw io.ReadWriter, tag tpmutil.Tag, cmd tpmutil.Command, in ...in return nil, err } if code != tpmutil.RCSuccess { - return nil, fmt.Errorf("response status 0x%x", code) + return nil, decodeResponse(code) } - return resp, nil + return resp, decodeResponse(code) } // concat is a helper for encoding functions that separately encode handle, @@ -1090,3 +1393,265 @@ func runCommand(rw io.ReadWriter, tag tpmutil.Tag, cmd tpmutil.Command, in ...in func concat(chunks ...[]byte) ([]byte, error) { return bytes.Join(chunks, nil), nil } + +func encodePCRExtend(pcr tpmutil.Handle, hashAlg Algorithm, hash tpmutil.RawBytes, password string) ([]byte, error) { + ha, err := tpmutil.Pack(pcr) + if err != nil { + return nil, err + } + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(password)}) + if err != nil { + return nil, err + } + pcrCount := uint32(1) + extend, err := tpmutil.Pack(pcrCount, hashAlg, hash) + if err != nil { + return nil, err + } + return concat(ha, auth, extend) +} + +// PCRExtend extends a value into the selected PCR +func PCRExtend(rw io.ReadWriter, pcr tpmutil.Handle, hashAlg Algorithm, hash []byte, password string) error { + cmd, err := encodePCRExtend(pcr, hashAlg, hash, password) + if err != nil { + return err + } + _, err = runCommand(rw, TagSessions, cmdPCRExtend, tpmutil.RawBytes(cmd)) + return err +} + +// ReadPCR reads the value of the given PCR. +func ReadPCR(rw io.ReadWriter, pcr int, hashAlg Algorithm) ([]byte, error) { + pcrSelection := PCRSelection{ + Hash: hashAlg, + PCRs: []int{pcr}, + } + pcrVals, err := ReadPCRs(rw, pcrSelection) + if err != nil { + return nil, fmt.Errorf("unable to read PCRs from TPM: %v", err) + } + pcrVal, present := pcrVals[pcr] + if !present { + return nil, fmt.Errorf("PCR %d value missing from response", pcr) + } + return pcrVal, nil +} + +// EncryptSymmetric encrypts data using a symmetric key. +// +// WARNING: This command performs low-level cryptographic operations. +// Secure use of this command is subtle and requires careful analysis. +// Please consult with experts in cryptography for how to use it securely. +// +// The iv is the initialization vector. The iv must not be empty and its size depends on the +// details of the symmetric encryption scheme. +// +// The data may be longer than block size, EncryptSymmetric will chain +// multiple TPM calls to encrypt the entire blob. +// +// Key handle should point at SymCipher object which is a child of the key (and +// not e.g. RSA key itself). +func EncryptSymmetric(rw io.ReadWriteCloser, keyAuth string, key tpmutil.Handle, iv, data []byte) ([]byte, error) { + return encryptDecryptSymmetric(rw, keyAuth, key, iv, data, false) +} + +// DecryptSymmetric decrypts data using a symmetric key. +// +// WARNING: This command performs low-level cryptographic operations. +// Secure use of this command is subtle and requires careful analysis. +// Please consult with experts in cryptography for how to use it securely. +// +// The iv is the initialization vector. The iv must not be empty and its size +// depends on the details of the symmetric encryption scheme. +// +// The data may be longer than block size, DecryptSymmetric will chain multiple +// TPM calls to decrypt the entire blob. +// +// Key handle should point at SymCipher object which is a child of the key (and +// not e.g. RSA key itself). +func DecryptSymmetric(rw io.ReadWriteCloser, keyAuth string, key tpmutil.Handle, iv, data []byte) ([]byte, error) { + return encryptDecryptSymmetric(rw, keyAuth, key, iv, data, true) +} + +func encodeEncryptDecrypt(keyAuth string, key tpmutil.Handle, iv, data tpmutil.U16Bytes, decrypt bool) ([]byte, error) { + ha, err := tpmutil.Pack(key) + if err != nil { + return nil, err + } + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(keyAuth)}) + if err != nil { + return nil, err + } + // Use encryption key's mode. + params, err := tpmutil.Pack(decrypt, AlgNull, iv, data) + if err != nil { + return nil, err + } + return concat(ha, auth, params) +} + +func encodeEncryptDecrypt2(keyAuth string, key tpmutil.Handle, iv, data tpmutil.U16Bytes, decrypt bool) ([]byte, error) { + ha, err := tpmutil.Pack(key) + if err != nil { + return nil, err + } + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(keyAuth)}) + if err != nil { + return nil, err + } + // Use encryption key's mode. + params, err := tpmutil.Pack(data, decrypt, AlgNull, iv) + if err != nil { + return nil, err + } + return concat(ha, auth, params) +} + +func decodeEncryptDecrypt(resp []byte) ([]byte, []byte, error) { + var paramSize uint32 + var out, nextIV tpmutil.U16Bytes + if _, err := tpmutil.Unpack(resp, ¶mSize, &out, &nextIV); err != nil { + return nil, nil, err + } + return out, nextIV, nil +} + +func encryptDecryptBlockSymmetric(rw io.ReadWriteCloser, keyAuth string, key tpmutil.Handle, iv, data []byte, decrypt bool) ([]byte, []byte, error) { + cmd, err := encodeEncryptDecrypt2(keyAuth, key, iv, data, decrypt) + if err != nil { + return nil, nil, err + } + resp, err := runCommand(rw, TagSessions, cmdEncryptDecrypt2, tpmutil.RawBytes(cmd)) + if err != nil { + fmt0Err, ok := err.(Error) + if ok && fmt0Err.Code == RCCommandCode { + // If TPM2_EncryptDecrypt2 is not supported, fall back to + // TPM2_EncryptDecrypt. + cmd, err := encodeEncryptDecrypt(keyAuth, key, iv, data, decrypt) + if err != nil { + return nil, nil, err + } + resp, err = runCommand(rw, TagSessions, cmdEncryptDecrypt, tpmutil.RawBytes(cmd)) + } + } + if err != nil { + return nil, nil, err + } + return decodeEncryptDecrypt(resp) +} + +func encryptDecryptSymmetric(rw io.ReadWriteCloser, keyAuth string, key tpmutil.Handle, iv, data []byte, decrypt bool) ([]byte, error) { + var out, block []byte + var err error + + for rest := data; len(rest) > 0; { + if len(rest) > maxDigestBuffer { + block, rest = rest[:maxDigestBuffer], rest[maxDigestBuffer:] + } else { + block, rest = rest, nil + } + block, iv, err = encryptDecryptBlockSymmetric(rw, keyAuth, key, iv, block, decrypt) + if err != nil { + return nil, err + } + out = append(out, block...) + } + + return out, nil +} + +func encodeRSAEncrypt(key tpmutil.Handle, message tpmutil.U16Bytes, scheme *AsymScheme, label string) ([]byte, error) { + ha, err := tpmutil.Pack(key) + if err != nil { + return nil, err + } + m, err := tpmutil.Pack(message) + if err != nil { + return nil, err + } + s, err := scheme.encode() + if err != nil { + return nil, err + } + if label != "" { + label += "\x00" + } + l, err := tpmutil.Pack(tpmutil.U16Bytes(label)) + if err != nil { + return nil, err + } + return concat(ha, m, s, l) +} + +func decodeRSAEncrypt(resp []byte) ([]byte, error) { + var out tpmutil.U16Bytes + _, err := tpmutil.Unpack(resp, &out) + return out, err +} + +// RSAEncrypt performs RSA encryption in the TPM according to RFC 3447. The key must be +// a (public) key loaded into the TPM beforehand. Note that when using OAEP with a label, +// a null byte is appended to the label and the null byte is included in the padding +// scheme. +func RSAEncrypt(rw io.ReadWriter, key tpmutil.Handle, message []byte, scheme *AsymScheme, label string) ([]byte, error) { + cmd, err := encodeRSAEncrypt(key, message, scheme, label) + if err != nil { + return nil, err + } + resp, err := runCommand(rw, TagNoSessions, cmdRSAEncrypt, tpmutil.RawBytes(cmd)) + if err != nil { + return nil, err + } + return decodeRSAEncrypt(resp) +} + +func encodeRSADecrypt(key tpmutil.Handle, password string, message tpmutil.U16Bytes, scheme *AsymScheme, label string) ([]byte, error) { + ha, err := tpmutil.Pack(key) + if err != nil { + return nil, err + } + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(password)}) + if err != nil { + return nil, err + } + m, err := tpmutil.Pack(message) + if err != nil { + return nil, err + } + s, err := scheme.encode() + if err != nil { + return nil, err + } + if label != "" { + label += "\x00" + } + l, err := tpmutil.Pack(tpmutil.U16Bytes(label)) + if err != nil { + return nil, err + } + return concat(ha, auth, m, s, l) +} + +func decodeRSADecrypt(resp []byte) ([]byte, error) { + var out tpmutil.U16Bytes + var paramSize uint32 + _, err := tpmutil.Unpack(resp, ¶mSize, &out) + return out, err +} + +// RSADecrypt performs RSA decryption in the TPM according to RFC 3447. The key must be +// a private RSA key in the TPM with FlagDecrypt set. Note that when using OAEP with a +// label, a null byte is appended to the label and the null byte is included in the +// padding scheme. +func RSADecrypt(rw io.ReadWriter, key tpmutil.Handle, password string, message []byte, scheme *AsymScheme, label string) ([]byte, error) { + cmd, err := encodeRSADecrypt(key, password, message, scheme, label) + if err != nil { + return nil, err + } + resp, err := runCommand(rw, TagSessions, cmdRSADecrypt, tpmutil.RawBytes(cmd)) + if err != nil { + return nil, err + } + return decodeRSADecrypt(resp) +} diff --git a/vendor/github.com/google/go-tpm/tpm2/tpm2_test.go b/vendor/github.com/google/go-tpm/tpm2/tpm2_test.go deleted file mode 100644 index 8d22d73..0000000 --- a/vendor/github.com/google/go-tpm/tpm2/tpm2_test.go +++ /dev/null @@ -1,563 +0,0 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tpm2 - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "flag" - "math/big" - "os" - "reflect" - "testing" - - "github.com/google/go-tpm/tpmutil" -) - -func TestMain(m *testing.M) { - flag.Parse() - os.Exit(m.Run()) -} - -var ( - // PCR7 is for SecureBoot. - pcrSelection = PCRSelection{Hash: AlgSHA1, PCRs: []int{7}} - defaultKeyParams = Public{ - Type: AlgRSA, - NameAlg: AlgSHA1, - Attributes: 0x00030072, - RSAParameters: &RSAParams{ - Symmetric: &SymScheme{ - Alg: AlgAES, - KeyBits: 128, - Mode: AlgCFB, - }, - KeyBits: 2048, - Exponent: uint32(0x00010001), - Modulus: big.NewInt(0), - }, - } - defaultPassword = "\x01\x02\x03\x04" - emptyPassword = "" -) - -func TestGetRandom(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - if _, err := GetRandom(rw, 16); err != nil { - t.Fatalf("GetRandom failed: %v", err) - } -} - -func TestReadPCRs(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - pcrs, err := ReadPCRs(rw, pcrSelection) - if err != nil { - t.Errorf("ReadPCRs failed: %s", err) - } - for pcr, val := range pcrs { - if empty := make([]byte, len(val)); reflect.DeepEqual(empty, val) { - t.Errorf("Value of PCR %d is empty", pcr) - } - } -} - -func TestReadClock(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - if _, _, err := ReadClock(rw); err != nil { - t.Fatalf("ReadClock failed: %s", err) - } - -} - -func TestGetCapability(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - if _, err := GetCapability(rw, CapabilityHandles, 1, 0x80000000); err != nil { - t.Fatalf("GetCapability failed: %s", err) - } -} - -func TestCombinedKeyTest(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - parentHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection, emptyPassword, defaultPassword, defaultKeyParams) - if err != nil { - t.Fatalf("CreatePrimary failed: %s", err) - } - defer FlushContext(rw, parentHandle) - - privateBlob, publicBlob, err := CreateKey(rw, parentHandle, pcrSelection, defaultPassword, defaultPassword, defaultKeyParams) - if err != nil { - t.Fatalf("CreateKey failed: %s", err) - } - - keyHandle, _, err := Load(rw, parentHandle, defaultPassword, publicBlob, privateBlob) - if err != nil { - t.Fatalf("Load failed: %s", err) - } - defer FlushContext(rw, keyHandle) - - if _, _, _, err := ReadPublic(rw, keyHandle); err != nil { - t.Fatalf("ReadPublic failed: %s", err) - } -} - -func TestCombinedEndorsementTest(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - parentHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection, emptyPassword, emptyPassword, defaultKeyParams) - if err != nil { - t.Fatalf("CreatePrimary failed: %s", err) - } - defer FlushContext(rw, parentHandle) - - privateBlob, publicBlob, err := CreateKey(rw, parentHandle, pcrSelection, emptyPassword, defaultPassword, defaultKeyParams) - if err != nil { - t.Fatalf("CreateKey failed: %s", err) - } - - keyHandle, _, err := Load(rw, parentHandle, emptyPassword, publicBlob, privateBlob) - if err != nil { - t.Fatalf("Load failed: %s", err) - } - defer FlushContext(rw, keyHandle) - - _, name, _, err := ReadPublic(rw, keyHandle) - if err != nil { - t.Fatalf("ReadPublic failed: %s", err) - } - - // Generate Credential - credential := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10} - credBlob, encryptedSecret0, err := MakeCredential(rw, parentHandle, credential, name) - if err != nil { - t.Fatalf("MakeCredential failed: %s", err) - } - - recoveredCredential1, err := ActivateCredential(rw, keyHandle, parentHandle, defaultPassword, emptyPassword, credBlob, encryptedSecret0) - if err != nil { - t.Fatalf("ActivateCredential failed: %s", err) - } - if bytes.Compare(credential, recoveredCredential1) != 0 { - t.Fatalf("Credential and recovered credential differ: got %v, want %v", recoveredCredential1, credential) - } -} - -func TestCombinedContextTest(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - rootHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection, emptyPassword, emptyPassword, defaultKeyParams) - if err != nil { - t.Fatalf("CreatePrimary failed: %v", err) - } - defer FlushContext(rw, rootHandle) - - // CreateKey (Quote Key) - quotePrivate, quotePublic, err := CreateKey(rw, rootHandle, pcrSelection, emptyPassword, emptyPassword, defaultKeyParams) - if err != nil { - t.Fatalf("CreateKey failed: %v", err) - } - - quoteHandle, _, err := Load(rw, rootHandle, emptyPassword, quotePublic, quotePrivate) - if err != nil { - t.Fatalf("Load failed: %v", err) - } - defer FlushContext(rw, quoteHandle) - - saveArea, err := ContextSave(rw, quoteHandle) - if err != nil { - t.Fatalf("ContextSave failed: %v", err) - } - FlushContext(rw, quoteHandle) - - quoteHandle, err = ContextLoad(rw, saveArea) - if err != nil { - t.Fatalf("Load failed: %v", err) - } -} - -func TestEvictControl(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - rootHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection, emptyPassword, emptyPassword, defaultKeyParams) - if err != nil { - t.Fatalf("CreatePrimary failed: %v", err) - } - defer FlushContext(rw, rootHandle) - - // CreateKey (Quote Key) - quotePrivate, quotePublic, err := CreateKey(rw, rootHandle, pcrSelection, emptyPassword, emptyPassword, defaultKeyParams) - if err != nil { - t.Fatalf("CreateKey failed: %v", err) - } - - quoteHandle, _, err := Load(rw, rootHandle, emptyPassword, quotePublic, quotePrivate) - if err != nil { - t.Fatalf("Load failed: %v", err) - } - defer FlushContext(rw, quoteHandle) - - persistentHandle := tpmutil.Handle(0x817FFFFF) - // Evict persistent key, if there is one already (e.g. last test run failed). - if err := EvictControl(rw, emptyPassword, HandleOwner, persistentHandle, persistentHandle); err != nil { - t.Logf("(expected) EvictControl failed: %v", err) - } - // Make key persistent. - if err := EvictControl(rw, emptyPassword, HandleOwner, quoteHandle, persistentHandle); err != nil { - t.Fatalf("EvictControl failed: %v", err) - } - // Evict persistent key. - if err := EvictControl(rw, emptyPassword, HandleOwner, persistentHandle, persistentHandle); err != nil { - t.Fatalf("EvictControl failed: %v", err) - } -} - -func TestHash(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - val := []byte("garmonbozia") - got, err := Hash(rw, AlgSHA256, val) - if err != nil { - t.Fatalf("Hash failed: %v", err) - } - want := sha256.Sum256(val) - - if !bytes.Equal(got, want[:]) { - t.Errorf("Hash(%q) returned %x, want %x", val, got, want) - } -} - -func TestLoadExternalPublicKey(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - run := func(t *testing.T, public Public, private Private) { - t.Helper() - - h, _, err := LoadExternal(rw, public, private, HandleNull) - if err != nil { - t.Fatal(err) - } - defer FlushContext(rw, h) - } - - t.Run("RSA", func(t *testing.T) { - pk, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatal(err) - } - rp := Public{ - Type: AlgRSA, - NameAlg: AlgSHA1, - Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, - RSAParameters: &RSAParams{ - Sign: &SigScheme{ - Alg: AlgRSASSA, - Hash: AlgSHA1, - }, - KeyBits: 2048, - Exponent: uint32(pk.PublicKey.E), - Modulus: pk.PublicKey.N, - }, - } - private := Private{ - Type: AlgRSA, - Sensitive: pk.Primes[0].Bytes(), - } - run(t, rp, private) - }) - t.Run("ECC", func(t *testing.T) { - pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Fatal(err) - } - public := Public{ - Type: AlgECC, - NameAlg: AlgSHA1, - Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, - ECCParameters: &ECCParams{ - Sign: &SigScheme{ - Alg: AlgECDSA, - Hash: AlgSHA1, - }, - CurveID: CurveNISTP256, - Point: ECPoint{X: pk.PublicKey.X, Y: pk.PublicKey.Y}, - }, - } - private := Private{ - Type: AlgECC, - Sensitive: pk.D.Bytes(), - } - run(t, public, private) - }) -} - -func TestCertify(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - params := Public{ - Type: AlgRSA, - NameAlg: AlgSHA256, - Attributes: FlagSignerDefault, - RSAParameters: &RSAParams{ - Sign: &SigScheme{ - Alg: AlgRSASSA, - Hash: AlgSHA256, - }, - KeyBits: 2048, - Modulus: big.NewInt(0), - }, - } - signerHandle, signerPub, err := CreatePrimary(rw, HandleOwner, pcrSelection, emptyPassword, defaultPassword, params) - if err != nil { - t.Fatalf("CreatePrimary(signer) failed: %s", err) - } - defer FlushContext(rw, signerHandle) - - subjectHandle, subjectPub, err := CreatePrimary(rw, HandlePlatform, pcrSelection, emptyPassword, defaultPassword, params) - if err != nil { - t.Fatalf("CreatePrimary(subject) failed: %s", err) - } - defer FlushContext(rw, subjectHandle) - - attest, sig, err := Certify(rw, defaultPassword, defaultPassword, subjectHandle, signerHandle, nil) - if err != nil { - t.Errorf("Certify failed: %s", err) - return - } - - attestHash := sha256.Sum256(attest) - if err := rsa.VerifyPKCS1v15(signerPub.(*rsa.PublicKey), crypto.SHA256, attestHash[:], sig); err != nil { - t.Errorf("Signature verification failed: %v", err) - } - - t.Run("DecodeAttestationData", func(t *testing.T) { - ad, err := DecodeAttestationData(attest) - if err != nil { - t.Fatal("DecodeAttestationData:", err) - } - params := Public{ - Type: AlgRSA, - NameAlg: AlgSHA256, - Attributes: FlagSignerDefault, - RSAParameters: &RSAParams{ - Sign: &SigScheme{ - Alg: AlgRSASSA, - Hash: AlgSHA256, - }, - KeyBits: 2048, - // Note: we don't include Exponent because CreatePrimary also - // returns Public without it. - Modulus: subjectPub.(*rsa.PublicKey).N, - }, - } - matches, err := ad.AttestedCertifyInfo.Name.MatchesPublic(params) - if err != nil { - t.Fatalf("AttestedCertifyInfo.Name.MatchesPublic error: %v", err) - } - if !matches { - t.Error("Name in AttestationData doesn't match Public structure of subject") - } - }) -} - -func TestCertifyExternalKey(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - params := Public{ - Type: AlgRSA, - NameAlg: AlgSHA256, - Attributes: FlagSignerDefault, - RSAParameters: &RSAParams{ - Sign: &SigScheme{ - Alg: AlgRSASSA, - Hash: AlgSHA256, - }, - KeyBits: 2048, - Modulus: big.NewInt(0), - }, - } - signerHandle, signerPub, err := CreatePrimary(rw, HandleOwner, pcrSelection, emptyPassword, defaultPassword, params) - if err != nil { - t.Fatalf("CreatePrimary(signer) failed: %s", err) - } - defer FlushContext(rw, signerHandle) - - run := func(t *testing.T, public Public, private Private) { - t.Helper() - subjectHandle, _, err := LoadExternal(rw, public, private, HandleNull) - if err != nil { - t.Fatalf("LoadExternal: %v", err) - } - defer FlushContext(rw, subjectHandle) - - attest, sig, err := Certify(rw, emptyPassword, defaultPassword, subjectHandle, signerHandle, nil) - if err != nil { - t.Errorf("Certify failed: %s", err) - return - } - - attestHash := sha256.Sum256(attest) - if err := rsa.VerifyPKCS1v15(signerPub.(*rsa.PublicKey), crypto.SHA256, attestHash[:], sig); err != nil { - t.Errorf("Signature verification failed: %v", err) - } - } - t.Run("RSA", func(t *testing.T) { - pk, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatal(err) - } - public := Public{ - Type: AlgRSA, - NameAlg: AlgSHA1, - Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, - RSAParameters: &RSAParams{ - Sign: &SigScheme{ - Alg: AlgRSASSA, - Hash: AlgSHA1, - }, - KeyBits: 2048, - Exponent: uint32(pk.PublicKey.E), - Modulus: pk.PublicKey.N, - }, - } - private := Private{ - Type: AlgRSA, - Sensitive: pk.Primes[0].Bytes(), - } - run(t, public, private) - }) - t.Run("ECC", func(t *testing.T) { - pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Fatal(err) - } - public := Public{ - Type: AlgECC, - NameAlg: AlgSHA1, - Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, - ECCParameters: &ECCParams{ - Sign: &SigScheme{ - Alg: AlgECDSA, - Hash: AlgSHA1, - }, - CurveID: CurveNISTP256, - Point: ECPoint{X: pk.PublicKey.X, Y: pk.PublicKey.Y}, - }, - } - private := Private{ - Type: AlgECC, - Sensitive: pk.D.Bytes(), - } - run(t, public, private) - }) -} - -func TestSign(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - - run := func(t *testing.T, pub Public) { - signerHandle, signerPub, err := CreatePrimary(rw, HandleOwner, pcrSelection, emptyPassword, defaultPassword, pub) - if err != nil { - t.Fatalf("CreatePrimary failed: %s", err) - } - defer FlushContext(rw, signerHandle) - - digest := sha256.Sum256([]byte("heyo")) - - var scheme *SigScheme - if pub.RSAParameters != nil { - scheme = pub.RSAParameters.Sign - } - if pub.ECCParameters != nil { - scheme = pub.ECCParameters.Sign - } - sig, err := Sign(rw, signerHandle, defaultPassword, digest[:], scheme) - if err != nil { - t.Fatalf("Sign failed: %s", err) - } - switch signerPub := signerPub.(type) { - case *rsa.PublicKey: - if err := rsa.VerifyPKCS1v15(signerPub, crypto.SHA256, digest[:], sig.RSA.Signature); err != nil { - t.Errorf("Signature verification failed: %v", err) - } - case *ecdsa.PublicKey: - if !ecdsa.Verify(signerPub, digest[:], sig.ECC.R, sig.ECC.S) { - t.Error("Signature verification failed") - } - } - } - - t.Run("RSA", func(t *testing.T) { - run(t, Public{ - Type: AlgRSA, - NameAlg: AlgSHA256, - Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, - RSAParameters: &RSAParams{ - Sign: &SigScheme{ - Alg: AlgRSASSA, - Hash: AlgSHA256, - }, - KeyBits: 2048, - Modulus: big.NewInt(0), - }, - }) - }) - t.Run("ECC", func(t *testing.T) { - run(t, Public{ - Type: AlgECC, - NameAlg: AlgSHA256, - Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, - ECCParameters: &ECCParams{ - Sign: &SigScheme{ - Alg: AlgECDSA, - Hash: AlgSHA256, - }, - CurveID: CurveNISTP256, - Point: ECPoint{X: big.NewInt(0), Y: big.NewInt(0)}, - }, - }) - }) -} - -func TestPCREvent(t *testing.T) { - rw := openTPM(t) - defer rw.Close() - debugPCR := uint32(16) - arbitraryBytes := []byte{1} - if err := PCREvent(rw, tpmutil.Handle(debugPCR), arbitraryBytes); err != nil { - t.Fatal(err) - } -} diff --git a/vendor/github.com/google/go-tpm/tpmutil/emulator_read_write_closer_test.go b/vendor/github.com/google/go-tpm/tpmutil/emulator_read_write_closer_test.go new file mode 100644 index 0000000..542e0c5 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/emulator_read_write_closer_test.go @@ -0,0 +1,197 @@ +// +build !windows + +// Copyright (c) 2019, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tpmutil + +import ( + "fmt" + "net" + "testing" + "time" +) + +// mockConn records the number of bytes that are read from it and written to it +// and tracks whether or not it has been closed. +type mockConn struct { + network string + path string + open bool +} + +// dialMockConn returns a mockConn that holds the given network and path info. +func dialMockConn(network, path string) (net.Conn, error) { + return &mockConn{ + network: network, + path: path, + open: true, + }, nil +} + +// Read implements a mock version of Read. +func (mc *mockConn) Read(b []byte) (int, error) { + // Always read zeros into the bytes for the given length. + for i := range b { + b[i] = 0 + } + return len(b), nil +} + +// Write implements a mock version of Write. +func (mc *mockConn) Write(b []byte) (int, error) { + return len(b), nil +} + +// Close implements a mock version of Close. +func (mc *mockConn) Close() error { + if !mc.open { + return fmt.Errorf("mockConn is already closed") + } + mc.open = false + return nil +} + +// LocalAddr returns nil. +func (mc *mockConn) LocalAddr() net.Addr { + return nil +} + +// RemoteAddr returns nil. +func (mc *mockConn) RemoteAddr() net.Addr { + return nil +} + +// SetDeadline returns nil. +func (mc *mockConn) SetDeadline(t time.Time) error { + return nil +} + +// SetReadDeadline returns nil. +func (mc *mockConn) SetReadDeadline(t time.Time) error { + return nil +} + +// SetWriteDeadline returns nil. +func (mc *mockConn) SetWriteDeadline(t time.Time) error { + return nil +} + +func newMockEmulator() *EmulatorReadWriteCloser { + path := "/dev/null/fake" + rwc := NewEmulatorReadWriteCloser(path) + rwc.dialer = dialMockConn + return rwc +} + +var ( + input = []byte(`input`) + output = make([]byte, 1) +) + +func TestEmulatorReadWriteCloserMultipleReads(t *testing.T) { + rwc := newMockEmulator() + n, err := rwc.Write(input) + if err != nil { + t.Errorf("failed to write: %v", err) + } + if n != len(input) { + t.Errorf("wrong number of bytes written: got %d, expected %d", n, len(input)) + } + + n, err = rwc.Read(output) + if err != nil { + t.Errorf("failed to read: %v", err) + } + if n != len(output) { + t.Errorf("wrong number of bytes read: got %d, expected %d", n, len(output)) + } + + n, err = rwc.Write(input) + if err != nil { + t.Errorf("failed to write: %v", err) + } + if n != len(input) { + t.Errorf("wrong number of bytes written: got %d, expected %d", n, len(input)) + } + + n, err = rwc.Read(output) + if err != nil { + t.Errorf("failed to read: %v", err) + } + if n != len(output) { + t.Errorf("wrong number of bytes read: got %d, expected %d", n, len(output)) + } +} + +func TestEmulatorReadWriteCloserClose(t *testing.T) { + rwc := newMockEmulator() + if err := rwc.Close(); err == nil { + t.Errorf("incorrectly closed a connection that hadn't been opened") + } + + if _, err := rwc.Write(input); err != nil { + t.Errorf("failed to write: %v", err) + } + + if err := rwc.Close(); err != nil { + t.Errorf("failed to close an open connection: %v", err) + } + + if err := rwc.Close(); err == nil { + t.Errorf("incorrectly closed a connection that had already been closed") + } +} + +func TestEmulatorReadWriteCloseReadAfterClose(t *testing.T) { + rwc := newMockEmulator() + if _, err := rwc.Write(input); err != nil { + t.Errorf("failed to write: %v", err) + } + + if err := rwc.Close(); err != nil { + t.Errorf("failed to close the connection: %v", err) + } + + if _, err := rwc.Read(output); err == nil { + t.Errorf("incorrectly read on a closed connection") + } +} + +func TestEmulatorReadWriteCloserReadBeforeWrite(t *testing.T) { + rwc := newMockEmulator() + b := make([]byte, 1) + if _, err := rwc.Read(b); err == nil { + t.Errorf("incorrectly read on a connection before writing") + } +} + +func TestEmulatorReadWriteCloserDoubleWrite(t *testing.T) { + rwc := newMockEmulator() + if _, err := rwc.Write(input); err != nil { + t.Errorf("failed to write: %v", err) + } + if _, err := rwc.Write(input); err == nil { + t.Errorf("incorrectly wrote a second time without reading in between") + } +} + +func TestEmulatorReadWriteCloserDialerError(t *testing.T) { + rwc := newMockEmulator() + rwc.dialer = func(_, _ string) (net.Conn, error) { return nil, fmt.Errorf("invalid") } + + if _, err := rwc.Write(input); err == nil { + t.Errorf("incorrectly wrote when the dialer returned an error") + } +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/encoding.go b/vendor/github.com/google/go-tpm/tpmutil/encoding.go index edac2ad..5983cc2 100644 --- a/vendor/github.com/google/go-tpm/tpmutil/encoding.go +++ b/vendor/github.com/google/go-tpm/tpmutil/encoding.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,88 +23,28 @@ import ( "reflect" ) -// lengthPrefixSize is the size in bytes of length prefix for byte slices. -// -// In TPM 1.2 this is 4 bytes. -// In TPM 2.0 this is 2 bytes. -var lengthPrefixSize int - -const ( - tpm12PrefixSize = 4 - tpm20PrefixSize = 2 +var ( + selfMarshalerType = reflect.TypeOf((*SelfMarshaler)(nil)).Elem() + handlesAreaType = reflect.TypeOf((*[]Handle)(nil)) ) -// UseTPM12LengthPrefixSize makes Pack/Unpack use TPM 1.2 encoding for byte -// arrays. -func UseTPM12LengthPrefixSize() { - lengthPrefixSize = tpm12PrefixSize -} - -// UseTPM20LengthPrefixSize makes Pack/Unpack use TPM 2.0 encoding for byte -// arrays. -func UseTPM20LengthPrefixSize() { - lengthPrefixSize = tpm20PrefixSize -} - -// packedSize computes the size of a sequence of types that can be passed to -// binary.Read or binary.Write. -func packedSize(elts ...interface{}) (int, error) { - var size int - for _, e := range elts { - v := reflect.ValueOf(e) - switch v.Kind() { - case reflect.Ptr: - s, err := packedSize(reflect.Indirect(v).Interface()) - if err != nil { - return 0, err - } - - size += s - case reflect.Struct: - for i := 0; i < v.NumField(); i++ { - s, err := packedSize(v.Field(i).Interface()) - if err != nil { - return 0, err - } - - size += s - } - case reflect.Slice: - switch s := e.(type) { - case []byte: - size += lengthPrefixSize + len(s) - case RawBytes: - size += len(s) - default: - return 0, fmt.Errorf("encoding of %T is not supported, only []byte and RawBytes slices are", e) - } - default: - s := binary.Size(e) - if s < 0 { - return 0, fmt.Errorf("can't calculate size of type %T", e) - } - - size += s - } - } - - return size, nil -} - // packWithHeader takes a header and a sequence of elements that are either of // fixed length or slices of fixed-length types and packs them into a single // byte array using binary.Write. It updates the CommandHeader to have the right // length. func packWithHeader(ch commandHeader, cmd ...interface{}) ([]byte, error) { hdrSize := binary.Size(ch) - bodySize, err := packedSize(cmd...) + body, err := Pack(cmd...) if err != nil { - return nil, fmt.Errorf("couldn't compute packed size for message body: %v", err) + return nil, fmt.Errorf("couldn't pack message body: %v", err) } + bodySize := len(body) ch.Size = uint32(hdrSize + bodySize) - in := []interface{}{ch} - in = append(in, cmd...) - return Pack(in...) + header, err := Pack(ch) + if err != nil { + return nil, fmt.Errorf("couldn't pack message header: %v", err) + } + return append(header, body...), nil } // Pack encodes a set of elements into a single byte array, using @@ -115,73 +55,99 @@ func packWithHeader(ch commandHeader, cmd ...interface{}) ([]byte, error) { // prepended length, to match how the TPM encodes variable-length arrays. If // you wish to add a byte slice without length prefix, use RawBytes. func Pack(elts ...interface{}) ([]byte, error) { - if lengthPrefixSize == 0 { - return nil, errors.New("lengthPrefixSize must be initialized") - } - buf := new(bytes.Buffer) if err := packType(buf, elts...); err != nil { return nil, err } - return buf.Bytes(), nil } -// packType recursively packs types the same way that encoding/binary does -// under binary.BigEndian, but with one difference: it packs a byte slice as a -// lengthPrefixSize size followed by the bytes. The function unpackType -// performs the inverse operation of unpacking slices stored in this manner and -// using encoding/binary for everything else. -func packType(buf io.Writer, elts ...interface{}) error { - for _, e := range elts { - v := reflect.ValueOf(e) - switch v.Kind() { - case reflect.Ptr: - if err := packType(buf, reflect.Indirect(v).Interface()); err != nil { - return err - } - case reflect.Struct: - for i := 0; i < v.NumField(); i++ { - if err := packType(buf, v.Field(i).Interface()); err != nil { - return err - } - } - case reflect.Slice: - switch s := e.(type) { - case []byte: - switch lengthPrefixSize { - case tpm20PrefixSize: - if err := binary.Write(buf, binary.BigEndian, uint16(len(s))); err != nil { - return err - } - case tpm12PrefixSize: - if err := binary.Write(buf, binary.BigEndian, uint32(len(s))); err != nil { - return err - } - default: - return fmt.Errorf("lengthPrefixSize is %d, must be either 2 or 4", lengthPrefixSize) - } - if err := binary.Write(buf, binary.BigEndian, s); err != nil { - return err - } - case RawBytes: - if err := binary.Write(buf, binary.BigEndian, s); err != nil { - return err - } - default: - return fmt.Errorf("only []byte and RawBytes slices are supported, got %T", e) - } - default: - if err := binary.Write(buf, binary.BigEndian, e); err != nil { +// tryMarshal attempts to use a TPMMarshal() method defined on the type +// to pack v into buf. True is returned if the method exists and the +// marshal was attempted. +func tryMarshal(buf io.Writer, v reflect.Value) (bool, error) { + t := v.Type() + if t.Implements(selfMarshalerType) { + if v.Kind() == reflect.Ptr && v.IsNil() { + return true, fmt.Errorf("cannot call TPMMarshal on a nil pointer of type %T", v) + } + return true, v.Interface().(SelfMarshaler).TPMMarshal(buf) + } + + // We might have a non-pointer struct field, but we dont have a + // pointer with which to implement the interface. + // If the pointer of the type implements the interface, we should be + // able to construct a value to call TPMMarshal() with. + // TODO(awly): Try and avoid blowing away private data by using Addr() instead of Set() + if reflect.PtrTo(t).Implements(selfMarshalerType) { + tmp := reflect.New(t) + tmp.Elem().Set(v) + return true, tmp.Interface().(SelfMarshaler).TPMMarshal(buf) + } + + return false, nil +} + +func packValue(buf io.Writer, v reflect.Value) error { + if v.Type() == handlesAreaType { + v = v.Convert(reflect.TypeOf((*handleList)(nil))) + } + if canMarshal, err := tryMarshal(buf, v); canMarshal { + return err + } + + switch v.Kind() { + case reflect.Ptr: + if v.IsNil() { + return fmt.Errorf("cannot pack nil %s", v.Type().String()) + } + return packValue(buf, v.Elem()) + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + if err := packValue(buf, f); err != nil { return err } } + default: + return binary.Write(buf, binary.BigEndian, v.Interface()) + } + return nil +} +func packType(buf io.Writer, elts ...interface{}) error { + for _, e := range elts { + if err := packValue(buf, reflect.ValueOf(e)); err != nil { + return err + } } return nil } +// tryUnmarshal attempts to use TPMUnmarshal() to perform the +// unpack, if the given value implements SelfMarshaler. +// True is returned if v implements SelfMarshaler & TPMUnmarshal +// was called, along with an error returned from TPMUnmarshal. +func tryUnmarshal(buf io.Reader, v reflect.Value) (bool, error) { + t := v.Type() + if t.Implements(selfMarshalerType) { + if v.Kind() == reflect.Ptr && v.IsNil() { + return true, fmt.Errorf("cannot call TPMUnmarshal on a nil pointer") + } + return true, v.Interface().(SelfMarshaler).TPMUnmarshal(buf) + } + + // We might have a non-pointer struct field, which is addressable, + // If the pointer of the type implements the interface, and the + // value is addressable, we should be able to call TPMUnmarshal(). + if v.CanAddr() && reflect.PtrTo(t).Implements(selfMarshalerType) { + return true, v.Addr().Interface().(SelfMarshaler).TPMUnmarshal(buf) + } + + return false, nil +} + // Unpack is a convenience wrapper around UnpackBuf. Unpack returns the number // of bytes read from b to fill elts and error, if any. func Unpack(b []byte, elts ...interface{}) (int, error) { @@ -191,6 +157,37 @@ func Unpack(b []byte, elts ...interface{}) (int, error) { return read, err } +func unpackValue(buf io.Reader, v reflect.Value) error { + if v.Type() == handlesAreaType { + v = v.Convert(reflect.TypeOf((*handleList)(nil))) + } + if didUnmarshal, err := tryUnmarshal(buf, v); didUnmarshal { + return err + } + + switch v.Kind() { + case reflect.Ptr: + if v.IsNil() { + return fmt.Errorf("cannot unpack nil %s", v.Type().String()) + } + return unpackValue(buf, v.Elem()) + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + if err := unpackValue(buf, f); err != nil { + return err + } + } + return nil + default: + // binary.Read can only set pointer values, so we need to take the address. + if !v.CanAddr() { + return fmt.Errorf("cannot unpack unaddressable leaf type %q", v.Type().String()) + } + return binary.Read(buf, binary.BigEndian, v.Addr().Interface()) + } +} + // UnpackBuf recursively unpacks types from a reader just as encoding/binary // does under binary.BigEndian, but with one difference: it unpacks a byte // slice by first reading an integer with lengthPrefixSize bytes, then reading @@ -199,88 +196,16 @@ func Unpack(b []byte, elts ...interface{}) (int, error) { func UnpackBuf(buf io.Reader, elts ...interface{}) error { for _, e := range elts { v := reflect.ValueOf(e) - k := v.Kind() - if k != reflect.Ptr { - return fmt.Errorf("all values passed to Unpack must be pointers, got %v", k) + if v.Kind() != reflect.Ptr { + return fmt.Errorf("non-pointer value %q passed to UnpackBuf", v.Type().String()) } - if v.IsNil() { - return errors.New("can't fill a nil pointer") + return errors.New("nil pointer passed to UnpackBuf") } - iv := reflect.Indirect(v) - switch iv.Kind() { - case reflect.Struct: - // Decompose the struct and copy over the values. - for i := 0; i < iv.NumField(); i++ { - if err := UnpackBuf(buf, iv.Field(i).Addr().Interface()); err != nil { - return err - } - } - case reflect.Slice: - var size int - _, isHandles := e.(*[]Handle) - - switch { - // []Handle always uses 2-byte length, even with TPM 1.2. - case isHandles: - var tmpSize uint16 - if err := binary.Read(buf, binary.BigEndian, &tmpSize); err != nil { - return err - } - size = int(tmpSize) - // TPM 2.0 - case lengthPrefixSize == tpm20PrefixSize: - var tmpSize uint16 - if err := binary.Read(buf, binary.BigEndian, &tmpSize); err != nil { - return err - } - size = int(tmpSize) - // TPM 1.2 - case lengthPrefixSize == tpm12PrefixSize: - var tmpSize uint32 - if err := binary.Read(buf, binary.BigEndian, &tmpSize); err != nil { - return err - } - size = int(tmpSize) - default: - return fmt.Errorf("lengthPrefixSize is %d, must be either 2 or 4", lengthPrefixSize) - } - - // A zero size is used by the TPM to signal that certain elements - // are not present. - if size == 0 { - continue - } - - // Make len(e) match size exactly. - switch b := e.(type) { - case *[]byte: - if len(*b) >= size { - *b = (*b)[:size] - } else { - *b = append(*b, make([]byte, size-len(*b))...) - } - case *[]Handle: - if len(*b) >= size { - *b = (*b)[:size] - } else { - *b = append(*b, make([]Handle, size-len(*b))...) - } - default: - return fmt.Errorf("can't fill pointer to %T, only []byte or []Handle slices", e) - } - - if err := binary.Read(buf, binary.BigEndian, e); err != nil { - return err - } - default: - if err := binary.Read(buf, binary.BigEndian, e); err != nil { - return err - } + if err := unpackValue(buf, v); err != nil { + return err } - } - return nil } diff --git a/vendor/github.com/google/go-tpm/tpmutil/encoding_test.go b/vendor/github.com/google/go-tpm/tpmutil/encoding_test.go index 671403f..4cf4aa1 100644 --- a/vendor/github.com/google/go-tpm/tpmutil/encoding_test.go +++ b/vendor/github.com/google/go-tpm/tpmutil/encoding_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,10 +22,6 @@ import ( "testing" ) -func init() { - UseTPM12LengthPrefixSize() -} - type invalidPacked struct { A []int B uint32 @@ -59,15 +55,6 @@ func testEncodingInvalidSlices(t *testing.T, f func(io.Writer, interface{}) erro } } -func TestEncodingPackedSizeInvalid(t *testing.T) { - f := func(w io.Writer, i interface{}) error { - _, err := packedSize(i) - return err - } - - testEncodingInvalidSlices(t, f) -} - func TestEncodingPackTypeInvalid(t *testing.T) { f := func(w io.Writer, i interface{}) error { return packType(w, i) @@ -88,28 +75,7 @@ type nestedPacked struct { type nestedSlice struct { A uint32 - S []byte -} - -func TestEncodingPackedSize(t *testing.T) { - buf := make([]byte, 10) - tests := []struct { - in interface{} - want int - }{ - {uint32(3), 4}, - {buf, 14}, - {&buf, 14}, - {simplePacked{137, 138}, 8}, - {nestedPacked{simplePacked{137, 138}, 139}, 12}, - {nestedSlice{137, buf}, 18}, - {[]byte(nil), 4}, - } - for _, tt := range tests { - if s, err := packedSize(tt.in); err != nil || s != tt.want { - t.Errorf("packedSize(%#v): %d, want %d", tt.in, s, tt.want) - } - } + S U32Bytes } func TestEncodingPackType(t *testing.T) { @@ -132,12 +98,15 @@ func TestEncodingPackType(t *testing.T) { } func TestEncodingPackTypeWriteFail(t *testing.T) { + u32WithOneByte := U32Bytes([]byte{1}) + u32Empty := U32Bytes([]byte(nil)) + tests := []struct { limit int in interface{} }{ - {4, []byte{1}}, - {3, []byte(nil)}, + {4, &u32WithOneByte}, + {3, &u32Empty}, } for _, tt := range tests { if err := packType(&limitedDiscard{tt.limit}, tt.in); err == nil { @@ -223,11 +192,11 @@ func TestEncodingInvalidUnpack(t *testing.T) { t.Fatal("UnpackBuf incorrectly deserialized into a non pointer") } - var b []byte + var b U32Bytes var empty []byte emptyBuf := bytes.NewBuffer(empty) if err := UnpackBuf(emptyBuf, &b); err == nil { - t.Fatal("UnpackBuf incorrectly deserialized an empty byte array into a byte slice") + t.Fatal("UnpackBuf incorrectly deserialized an empty byte array into U32Bytes") } // Try to deserialize a byte array that has a length but not enough bytes. @@ -247,9 +216,37 @@ func TestEncodingInvalidUnpack(t *testing.T) { } +func TestSelfMarshaler(t *testing.T) { + var empty16 U16Bytes + var empty32 U32Bytes + subTests := []struct { + encoded []byte + decoded interface{} + }{ + {[]byte{0, 0}, &empty16}, + {[]byte{0, 1, 137}, &empty16}, + {[]byte{0, 0, 0, 0}, &empty32}, + {[]byte{0, 0, 0, 1, 137}, &empty32}, + } + for _, st := range subTests { + t.Logf("Attempting to Marshal/Unmarshal %#v into %T", st.encoded, st.decoded) + buffer := bytes.NewBuffer(st.encoded) + if err := UnpackBuf(buffer, st.decoded); err != nil { + t.Fatalf("UnpackBuf failed: %v", err) + } + packed, err := Pack(st.decoded) + if err != nil { + t.Fatalf("Pack failed: %v", err) + } + if !bytes.Equal(packed, st.encoded) { + t.Fatalf("Pack failed: got %#v, want: %#v", packed, st.encoded) + } + } +} + func TestEncodingUnpack(t *testing.T) { // Deserialize the empty byte array. - var b []byte + var b U32Bytes // The slice ui represents uint32(0), which is the length of an empty byte array. ui := []byte{0, 0, 0, 0} uiBuf := bytes.NewBuffer(ui) @@ -264,7 +261,7 @@ func TestEncodingUnpack(t *testing.T) { t.Fatal("UnpackBuf failed to unpack a byte array with a single value in it") } - if !bytes.Equal(b, []byte{137}) { + if !bytes.Equal([]byte(b), []byte{137}) { t.Fatal("UnpackBuf unpacked a small byte array incorrectly") } @@ -301,7 +298,7 @@ func TestEncodingUnpack(t *testing.T) { } ns := nestedSlice{137, b} - bns, err := Pack(ns) + bns, err := Pack(&ns) if err != nil { t.Fatal("Couldn't pack a struct with a nested byte slice:", err) } @@ -310,6 +307,8 @@ func TestEncodingUnpack(t *testing.T) { t.Fatal("Couldn't unpacked a struct with a nested slice:", err) } if ns.A != ns2.A || !bytes.Equal(ns.S, ns2.S) { + t.Logf("orginal = %+v", ns) + t.Logf("decoded = %+v", ns2) t.Fatal("Unpacked struct with nested slice didn't match the original") } @@ -349,3 +348,19 @@ func TestPartialUnpack(t *testing.T) { t.Errorf("sum of bytes read doesn't ad up to total packed size: got %d+%d=%d, want %d", read1, read2, read1+read2, len(buf)) } } + +func TestUnpackHandlesArea(t *testing.T) { + buf := []byte{ + 0, 2, + 0, 0, 0, 1, + 0, 0, 5, 57, + } + var out []Handle + + if _, err := Unpack(buf, &out); err != nil { + t.Fatalf("Unpack(%v, %T) failed: %v", buf, &out, err) + } + if want := []Handle{1, 1337}; !reflect.DeepEqual(out, want) { + t.Errorf("Unpack(%v, %T): %T = %v, want %v", buf, &out, out, out, want) + } +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/mssim/mssim.go b/vendor/github.com/google/go-tpm/tpmutil/mssim/mssim.go new file mode 100644 index 0000000..2ec05e5 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/mssim/mssim.go @@ -0,0 +1,193 @@ +// Copyright (c) 2018, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package mssim implements the Microsoft simulator TPM2 Transmission Interface +// +// The Microsoft simulator TPM Command Transmission Interface (TCTI) is a +// remote procedure interface donated to the TPM2 Specification by Microsoft. +// Its primary implementation is the tpm_server maintained by IBM. +// +// https://sourceforge.net/projects/ibmswtpm2/ +// +// This package implements client code to communicate with server code described +// in the document "TPM2 Specification Part 4: Supporting Routines – Code" +// +// https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-4-Supporting-Routines-01.38-code.pdf +package mssim + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "net" +) + +// Constants defined in "D.3.2. Typedefs and Defines" +const ( + tpmSignalPowerOn uint32 = 1 + tpmSignalPowerOff uint32 = 2 + tpmSendCommand uint32 = 8 + tpmSignalNVOn uint32 = 11 + tpmSessionEnd uint32 = 20 +) + +// Config holds configuration parameters for connecting to the simulator. +type Config struct { + // Addresses of the command and platform handlers. + // + // Defaults to port 2321 and 2322 on localhost. + CommandAddress string + PlatformAddress string +} + +// Open creates connections to the simulator's command and platform ports and +// power cycles the simulator to initialize it. +func Open(config Config) (*Conn, error) { + cmdAddr := config.CommandAddress + if cmdAddr == "" { + cmdAddr = "127.0.0.1:2321" + } + + platformAddr := config.PlatformAddress + if platformAddr == "" { + platformAddr = "127.0.0.1:2322" + } + + conn, err := net.Dial("tcp", platformAddr) + if err != nil { + return nil, fmt.Errorf("dial platform address: %v", err) + } + defer conn.Close() + + // Startup the simulator. This order of commands copies IBM's TPM2 tool's + // "powerup" command line tool and will reset the simulator. + // + // https://sourceforge.net/projects/ibmtpm20tss/ + + if err := sendPlatformCommand(conn, tpmSignalPowerOff); err != nil { + return nil, fmt.Errorf("power off platform command failed: %v", err) + } + if err := sendPlatformCommand(conn, tpmSignalPowerOn); err != nil { + return nil, fmt.Errorf("power on platform command failed: %v", err) + } + if err := sendPlatformCommand(conn, tpmSignalNVOn); err != nil { + return nil, fmt.Errorf("nv on platform command failed: %v", err) + } + + // Gracefully close the connection. + if err := binary.Write(conn, binary.BigEndian, tpmSessionEnd); err != nil { + return nil, fmt.Errorf("shutdown platform connection failed: %v", err) + } + + cmdConn, err := net.Dial("tcp", cmdAddr) + if err != nil { + return nil, fmt.Errorf("dial command address: %v", err) + } + + return &Conn{conn: cmdConn}, nil +} + +// sendPlatformCommand sends device management commands to the simulator. +// +// See: "D.4.3.2. PlatformServer()" +func sendPlatformCommand(conn net.Conn, u uint32) error { + if err := binary.Write(conn, binary.BigEndian, u); err != nil { + return fmt.Errorf("write platform command: %v", err) + } + + var rc uint32 + if err := binary.Read(conn, binary.BigEndian, &rc); err != nil { + return fmt.Errorf("read platform command: %v", err) + } + if rc != 0 { + return fmt.Errorf("unexpected platform command response code: 0x%x", rc) + } + return nil +} + +// Conn is a Microsoft Simulator client that can be used as a connection for the +// tpm2 package. +type Conn struct { + // Cached connection + conn net.Conn + + // Response bytes left over from the previous read. + prevRead *bytes.Reader +} + +// Read a response from the simulator. If the response is longer than the provided +// buffer, the remainder will be cached for the next read. +func (c *Conn) Read(b []byte) (int, error) { + if c.prevRead != nil && c.prevRead.Len() > 0 { + return c.prevRead.Read(b) + } + + // Response frame: + // - uint32 (size of response) + // - []byte (response) + // - uint32 (always 0) + var respLen uint32 + if err := binary.Read(c.conn, binary.BigEndian, &respLen); err != nil { + return 0, fmt.Errorf("read MS simulator response header: %v", err) + } + + resp := make([]byte, int(respLen)) + if _, err := io.ReadFull(c.conn, resp[:]); err != nil { + return 0, fmt.Errorf("read MS simulator response: %v", err) + } + + var rc uint32 + if err := binary.Read(c.conn, binary.BigEndian, &rc); err != nil { + return 0, fmt.Errorf("read MS simulator return code: %v", err) + } + if rc != 0 { + return 0, fmt.Errorf("MS simulator returned invalid return code: 0x%x", rc) + } + + c.prevRead = bytes.NewReader(resp) + return c.prevRead.Read(b) +} + +// Write a raw command to the simulator. Commands must be written in a single call +// to Write. Commands split over multiple calls will result in multiple framed +// requests. +func (c *Conn) Write(b []byte) (int, error) { + // See: D.4.3.12. TpmServer() + buff := &bytes.Buffer{} + // "send command" flag + binary.Write(buff, binary.BigEndian, tpmSendCommand) + // locality 0 + buff.WriteByte(0) + // size of the command + binary.Write(buff, binary.BigEndian, uint32(len(b))) + // raw command + buff.Write(b) + + if _, err := buff.WriteTo(c.conn); err != nil { + return 0, fmt.Errorf("write MS simulator command: %v", err) + } + return len(b), nil +} + +// Close closes any outgoing connections to the TPM simulator. +func (c *Conn) Close() error { + // See: D.4.3.12. TpmServer() + // Gracefully close the connection. + if err := binary.Write(c.conn, binary.BigEndian, tpmSessionEnd); err != nil { + c.conn.Close() + return fmt.Errorf("shutdown platform connection failed: %v", err) + } + return c.conn.Close() +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/poll_other.go b/vendor/github.com/google/go-tpm/tpmutil/poll_other.go new file mode 100644 index 0000000..6840f6f --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/poll_other.go @@ -0,0 +1,10 @@ +// +build !linux,!darwin + +package tpmutil + +import ( + "os" +) + +// Not implemented on Windows. +func poll(f *os.File) error { return nil } diff --git a/vendor/github.com/google/go-tpm/tpmutil/poll_unix.go b/vendor/github.com/google/go-tpm/tpmutil/poll_unix.go new file mode 100644 index 0000000..92e8270 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/poll_unix.go @@ -0,0 +1,42 @@ +// +build linux darwin + +package tpmutil + +import ( + "fmt" + "os" + "syscall" + "unsafe" +) + +type pollFD struct { + fd int32 + events int16 + revents int16 +} + +// poll blocks until the file descriptior is ready for reading or an error occurs. +func poll(f *os.File) error { + var ( + fd = &pollFD{ + fd: int32(f.Fd()), + events: 0x1, // POLLIN + } + numFD = 1 + timeoutMS = -1 // Do not set a timeout + ) + _, _, errno := syscall.Syscall(syscall.SYS_POLL, uintptr(unsafe.Pointer(fd)), uintptr(numFD), uintptr(timeoutMS)) + // Convert errno into an error, otherwise err != nil checks up the stack + // will hit unexpectedly on 0 errno. + var err error + if errno != 0 { + err = errno + return err + } + // revents is filled in by the kernel. + // If the expected event happened, revents should match events. + if fd.revents != fd.events { + return fmt.Errorf("unexpected poll revents 0x%x", fd.revents) + } + return nil +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/poll_unix_test.go b/vendor/github.com/google/go-tpm/tpmutil/poll_unix_test.go new file mode 100644 index 0000000..884f3af --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/poll_unix_test.go @@ -0,0 +1,27 @@ +// +build linux darwin + +package tpmutil + +import ( + "os" + "testing" +) + +func TestPoll(t *testing.T) { + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + defer r.Close() + defer w.Close() + + if _, err := w.Write([]byte("hi")); err != nil { + t.Fatalf("error writing to pipe: %v", err) + } + if err := poll(r); err != nil { + t.Errorf("error polling reader side of the pipe: %v", err) + } + if err := r.Close(); err != nil { + t.Fatalf("error closing reader side of the pipe: %v", err) + } +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/run.go b/vendor/github.com/google/go-tpm/tpmutil/run.go index 381c19a..21a9c66 100644 --- a/vendor/github.com/google/go-tpm/tpmutil/run.go +++ b/vendor/github.com/google/go-tpm/tpmutil/run.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,15 +13,12 @@ // limitations under the License. // Package tpmutil provides common utility functions for both TPM 1.2 and TPM 2.0 devices. -// -// Users should call either UseTPM12LengthPrefixSize or -// UseTPM20LengthPrefixSize before using this package, depending on their type -// of TPM device. package tpmutil import ( "errors" "io" + "os" ) // maxTPMResponse is the largest possible response from the TPM. We need to know @@ -38,7 +35,6 @@ func RunCommand(rw io.ReadWriter, tag Tag, cmd Command, in ...interface{}) ([]by if rw == nil { return nil, 0, errors.New("nil TPM handle") } - ch := commandHeader{tag, 0, cmd} inb, err := packWithHeader(ch, in...) if err != nil { @@ -49,6 +45,14 @@ func RunCommand(rw io.ReadWriter, tag Tag, cmd Command, in ...interface{}) ([]by return nil, 0, err } + // If the TPM is a real device, it may not be ready for reading immediately after writing + // the command. Wait until the file descriptor is ready to be read from. + if f, ok := rw.(*os.File); ok { + if err = poll(f); err != nil { + return nil, 0, err + } + } + outb := make([]byte, maxTPMResponse) outlen, err := rw.Read(outb) if err != nil { diff --git a/vendor/github.com/google/go-tpm/tpmutil/run_other.go b/vendor/github.com/google/go-tpm/tpmutil/run_other.go new file mode 100644 index 0000000..1b8dc2e --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/run_other.go @@ -0,0 +1,111 @@ +// +build !windows + +// Copyright (c) 2018, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tpmutil + +import ( + "fmt" + "io" + "net" + "os" +) + +// OpenTPM opens a channel to the TPM at the given path. If the file is a +// device, then it treats it like a normal TPM device, and if the file is a +// Unix domain socket, then it opens a connection to the socket. +func OpenTPM(path string) (io.ReadWriteCloser, error) { + // If it's a regular file, then open it + var rwc io.ReadWriteCloser + fi, err := os.Stat(path) + if err != nil { + return nil, err + } + + if fi.Mode()&os.ModeDevice != 0 { + var f *os.File + f, err = os.OpenFile(path, os.O_RDWR, 0600) + if err != nil { + return nil, err + } + rwc = io.ReadWriteCloser(f) + } else if fi.Mode()&os.ModeSocket != 0 { + rwc = NewEmulatorReadWriteCloser(path) + } else { + return nil, fmt.Errorf("unsupported TPM file mode %s", fi.Mode().String()) + } + + return rwc, nil +} + +// dialer abstracts the net.Dial call so test code can provide its own net.Conn +// implementation. +type dialer func(network, path string) (net.Conn, error) + +// EmulatorReadWriteCloser manages connections with a TPM emulator over a Unix +// domain socket. These emulators often operate in a write/read/disconnect +// sequence, so the Write method always connects, and the Read method always +// closes. EmulatorReadWriteCloser is not thread safe. +type EmulatorReadWriteCloser struct { + path string + conn net.Conn + dialer dialer +} + +// NewEmulatorReadWriteCloser stores information about a Unix domain socket to +// write to and read from. +func NewEmulatorReadWriteCloser(path string) *EmulatorReadWriteCloser { + return &EmulatorReadWriteCloser{ + path: path, + dialer: net.Dial, + } +} + +// Read implements io.Reader by reading from the Unix domain socket and closing +// it. +func (erw *EmulatorReadWriteCloser) Read(p []byte) (int, error) { + // Read is always the second operation in a Write/Read sequence. + if erw.conn == nil { + return 0, fmt.Errorf("Must call Write then Read in an alternating sequence") + } + n, err := erw.conn.Read(p) + erw.conn.Close() + erw.conn = nil + return n, err +} + +// Write implements io.Writer by connecting to the Unix domain socket and +// writing. +func (erw *EmulatorReadWriteCloser) Write(p []byte) (int, error) { + if erw.conn != nil { + return 0, fmt.Errorf("Must call Write then Read in an alternating sequence") + } + var err error + erw.conn, err = erw.dialer("unix", erw.path) + if err != nil { + return 0, err + } + return erw.conn.Write(p) +} + +// Close implements io.Closer by closing the Unix domain socket if one is open. +func (erw *EmulatorReadWriteCloser) Close() error { + if erw.conn == nil { + return fmt.Errorf("Cannot call Close when no connection is open") + } + err := erw.conn.Close() + erw.conn = nil + return err +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/run_windows.go b/vendor/github.com/google/go-tpm/tpmutil/run_windows.go index a2cd10e..f355b81 100644 --- a/vendor/github.com/google/go-tpm/tpmutil/run_windows.go +++ b/vendor/github.com/google/go-tpm/tpmutil/run_windows.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,120 +15,31 @@ package tpmutil import ( - "fmt" "io" - "syscall" - "unsafe" -) - -// Tbs.dll provides an API for making calls to the TPM: -// https://docs.microsoft.com/en-us/windows/desktop/TBS/tpm-base-services-portal -var ( - tbsDLL = syscall.NewLazyDLL("Tbs.dll") - tbsCreateContext = tbsDLL.NewProc("Tbsi_Context_Create") - tbsSubmitCommand = tbsDLL.NewProc("Tbsip_Submit_Command") - tbsContextClose = tbsDLL.NewProc("Tbsip_Context_Close") -) - -// tbsContextParams2 specifies the version of TPM and TBS implementation: -// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/ns-tbs-tdtbs_context_params2 -type tbsContextParams2 struct { - version uint32 - flags uint32 -} -// tbs.h contains constants used in the TBS library: -// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.10240.0/shared/tbs.h -const ( - tpm2Version uint32 = 2 // value of TPM_VERSION_20 tbs.h constant - bothTPMVersionsFlag uint32 = 6 // value of union struct to include TPM 1.2 and 2.0 (see tbsContextParams2) - tbsCommandLocalityZero uintptr = 0 // value of TBS_COMMAND_LOCALITY_ZERO tbs.h constant + "github.com/google/go-tpm/tpmutil/tbs" ) -// CommandPriority Parameter Docs: -// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsip_submit_command#parameters -type CommandPriority uint32 - -// CommandPriority is used to determine which pending command to submit whenever the TPM is free: -// https://docs.microsoft.com/en-us/windows/desktop/tbs/command-scheduling -const ( - LowPriority CommandPriority = 100 // For low priority application use. - NormalPriority CommandPriority = 200 // For normal priority application use. - HighPriority CommandPriority = 300 // For high priority application use. - SystemPriority CommandPriority = 400 // For system tasks that access the TPM. -) - -// TBS Error Codes: -// https://docs.microsoft.com/en-us/windows/desktop/TBS/tbs-return-codes -var errMap = map[uintptr]string{ - 0x80284001: "An internal software error occurred.", - 0x80284002: "One or more parameter values are not valid.", - 0x80284003: "A specified output pointer is bad.", - 0x80284004: "The specified context handle does not refer to a valid context.", - 0x80284005: "The specified output buffer is too small.", - 0x80284006: "An error occurred while communicating with the TPM.", - 0x80284007: "A context parameter that is not valid was passed when attempting to create a TBS context.", - 0x80284008: "The TBS service is not running and could not be started.", - 0x80284009: "A new context could not be created because there are too many open contexts.", - 0x8028400A: "A new virtual resource could not be created because there are too many open virtual resources.", - 0x8028400B: "The TBS service has been started but is not yet running.", - 0x8028400C: "The physical presence interface is not supported.", - 0x8028400D: "The command was canceled.", - 0x8028400E: "The input or output buffer is too large.", - 0x8028400F: "A compatible Trusted Platform Module (TPM) Security Device cannot be found on this computer.", - 0x80284010: "The TBS service has been disabled.", - 0x80284011: "The TBS event log is not available.", - 0x80284012: "The caller does not have the appropriate rights to perform the requested operation.", - 0x80284013: "The TPM provisioning action is not allowed by the specified flags.", - 0x80284014: "The Physical Presence Interface of this firmware does not support the requested method.", - 0x80284015: "The requested TPM OwnerAuth value was not found.", -} - -func tbsError(err uintptr) error { - if err == 0 { - return nil - } - if description, ok := errMap[err]; ok { - return fmt.Errorf("TBS Error %v: %s", err, description) - } - return fmt.Errorf("Unrecognized TBS Error %v", err) -} - // winTPMBuffer is a ReadWriteCloser to access the TPM in Windows. type winTPMBuffer struct { - context uintptr + context tbs.Context outBuffer []byte - priority CommandPriority } -// Executes the TPM command specified by commandBuffer, returning the number of bytes in the command -// and any error code returned by executing the TPM command. Command response can be read by calling -// Read(). +// Executes the TPM command specified by commandBuffer (at Normal Priority), returning the number +// of bytes in the command and any error code returned by executing the TPM command. Command +// response can be read by calling Read(). func (rwc *winTPMBuffer) Write(commandBuffer []byte) (int, error) { // TPM spec defines longest possible response to be maxTPMResponse. - outBufferLen := maxTPMResponse - rwc.outBuffer = rwc.outBuffer[:outBufferLen] + rwc.outBuffer = rwc.outBuffer[:maxTPMResponse] - // TBS_RESULT Tbsip_Submit_Command( - // _In_ TBS_HCONTEXT hContext, - // _In_ TBS_COMMAND_LOCALITY Locality, - // _In_ TBS_COMMAND_PRIORITY Priority, - // _In_ const PCBYTE *pabCommand, - // _In_ UINT32 cbCommand, - // _Out_ PBYTE *pabResult, - // _Inout_ UINT32 *pcbOutput - // ); - errResp, _, _ := tbsSubmitCommand.Call( - rwc.context, - tbsCommandLocalityZero, // Windows currently only supports TBS_COMMAND_LOCALITY_ZERO. - uintptr(rwc.priority), - uintptr(unsafe.Pointer(&(commandBuffer[0]))), - uintptr(len(commandBuffer)), - uintptr(unsafe.Pointer(&(rwc.outBuffer[0]))), - uintptr(unsafe.Pointer(&outBufferLen)), + outBufferLen, err := rwc.context.SubmitCommand( + tbs.NormalPriority, + commandBuffer, + rwc.outBuffer, ) - if err := tbsError(errResp); err != nil { + if err != nil { rwc.outBuffer = rwc.outBuffer[:0] return 0, err } @@ -143,38 +54,31 @@ func (rwc *winTPMBuffer) Read(responseBuffer []byte) (int, error) { return 0, io.EOF } lenCopied := copy(responseBuffer, rwc.outBuffer) - // Implements same behavior as linux "/dev/tpm0": discard unread components after read. - rwc.outBuffer = rwc.outBuffer[:0] + // Cut out the piece of slice which was just read out, maintaining original slice capacity. + rwc.outBuffer = append(rwc.outBuffer[:0], rwc.outBuffer[lenCopied:]...) return lenCopied, nil } func (rwc *winTPMBuffer) Close() error { - // TBS_RESULT Tbsip_Context_Close( - // _In_ TBS_HCONTEXT hContext - // ); - errResp, _, _ := tbsContextClose.Call(rwc.context) - return tbsError(errResp) + return rwc.context.Close() } // OpenTPM creates a new instance of a ReadWriteCloser which can interact with a -// Windows TPM. OpenTPM takes in the a CommandPriority at which to run commands. -func OpenTPM(commandPriority CommandPriority) (io.ReadWriteCloser, error) { - params := tbsContextParams2{ - version: tpm2Version, - flags: bothTPMVersionsFlag, +// Windows TPM. +func OpenTPM() (io.ReadWriteCloser, error) { + tpmContext, err := tbs.CreateContext(tbs.TPMVersion20, tbs.IncludeTPM12|tbs.IncludeTPM20) + rwc := &winTPMBuffer{ + context: tpmContext, + outBuffer: make([]byte, 0, maxTPMResponse), } + return rwc, err +} - rwc := winTPMBuffer{ +// FromContext creates a new instance of a ReadWriteCloser which can +// interact with a Windows TPM, using the specified TBS handle. +func FromContext(ctx tbs.Context) io.ReadWriteCloser { + return &winTPMBuffer{ + context: ctx, outBuffer: make([]byte, 0, maxTPMResponse), - priority: commandPriority, } - // TBS_RESULT Tbsi_Context_Create( - // _In_ PCTBS_CONTEXT_PARAMS pContextParams, - // _Out_ PTBS_HCONTEXT *phContext - // ); - errResp, _, _ := tbsCreateContext.Call( - uintptr(unsafe.Pointer(¶ms)), - uintptr(unsafe.Pointer(&rwc.context)), - ) - return &rwc, tbsError(errResp) } diff --git a/vendor/github.com/google/go-tpm/tpmutil/structures.go b/vendor/github.com/google/go-tpm/tpmutil/structures.go index d283687..c67b51a 100644 --- a/vendor/github.com/google/go-tpm/tpmutil/structures.go +++ b/vendor/github.com/google/go-tpm/tpmutil/structures.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018, Google Inc. All rights reserved. +// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,11 +14,103 @@ package tpmutil +import ( + "encoding/binary" + "fmt" + "io" +) + // RawBytes is for Pack and RunCommand arguments that are already encoded. // Compared to []byte, RawBytes will not be prepended with slice length during // encoding. type RawBytes []byte +// U16Bytes is a byte slice with a 16-bit header +type U16Bytes []byte + +// TPMMarshal packs U16Bytes +func (b *U16Bytes) TPMMarshal(out io.Writer) error { + size := uint16(len([]byte(*b))) + if err := binary.Write(out, binary.BigEndian, size); err != nil { + return err + } + + n, err := out.Write(*b) + if err != nil { + return err + } + if n != int(size) { + return fmt.Errorf("unable to write all contents of U16Bytes") + } + return nil +} + +// TPMUnmarshal unpacks a U16Bytes +func (b *U16Bytes) TPMUnmarshal(in io.Reader) error { + var tmpSize uint16 + if err := binary.Read(in, binary.BigEndian, &tmpSize); err != nil { + return err + } + size := int(tmpSize) + if len(*b) >= size { + *b = (*b)[:size] + } else { + *b = append(*b, make([]byte, size-len(*b))...) + } + + n, err := in.Read(*b) + if err != nil { + return err + } + if n != size { + return fmt.Errorf("unable to read all contents in to U16Bytes") + } + return nil +} + +// U32Bytes is a byte slice with a 32-bit header +type U32Bytes []byte + +// TPMMarshal packs U32Bytes +func (b *U32Bytes) TPMMarshal(out io.Writer) error { + size := uint32(len([]byte(*b))) + if err := binary.Write(out, binary.BigEndian, size); err != nil { + return err + } + + n, err := out.Write(*b) + if err != nil { + return err + } + if n != int(size) { + return fmt.Errorf("unable to write all contents of U32Bytes") + } + return nil +} + +// TPMUnmarshal unpacks a U32Bytes +func (b *U32Bytes) TPMUnmarshal(in io.Reader) error { + var tmpSize uint32 + if err := binary.Read(in, binary.BigEndian, &tmpSize); err != nil { + return err + } + size := int(tmpSize) + if len(*b) >= size { + *b = (*b)[:size] + } else { + *b = append(*b, make([]byte, size-len(*b))...) + } + + n, err := in.Read(*b) + if err != nil { + return err + } + if n != size { + return fmt.Errorf("unable to read all contents in to U32Bytes") + } + return nil +} + // Tag is a command tag. type Tag uint16 @@ -48,3 +140,32 @@ type responseHeader struct { // A Handle is a reference to a TPM object. type Handle uint32 + +type handleList []Handle + +func (l *handleList) TPMMarshal(out io.Writer) error { + return fmt.Errorf("TPMMarhsal on []Handle is not supported yet") +} + +func (l *handleList) TPMUnmarshal(in io.Reader) error { + var numHandles uint16 + if err := binary.Read(in, binary.BigEndian, &numHandles); err != nil { + return err + } + + // Make len(e) match size exactly. + size := int(numHandles) + if len(*l) >= size { + *l = (*l)[:size] + } else { + *l = append(*l, make([]Handle, size-len(*l))...) + } + return binary.Read(in, binary.BigEndian, *l) +} + +// SelfMarshaler allows custom types to override default encoding/decoding +// behavior in Pack, Unpack and UnpackBuf. +type SelfMarshaler interface { + TPMMarshal(out io.Writer) error + TPMUnmarshal(in io.Reader) error +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/tbs/tbs_windows.go b/vendor/github.com/google/go-tpm/tpmutil/tbs/tbs_windows.go new file mode 100644 index 0000000..4e90490 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/tbs/tbs_windows.go @@ -0,0 +1,250 @@ +// Copyright (c) 2018, Google LLC All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package tbs provides an low-level interface directly mapping to Windows +// Tbs.dll system library commands: +// https://docs.microsoft.com/en-us/windows/desktop/TBS/tpm-base-services-portal +// Public field descriptions contain links to the high-level Windows documentation. +package tbs + +import ( + "fmt" + "syscall" + "unsafe" +) + +// Context references the current TPM context +type Context uintptr + +// Version of TPM being used by the application. +type Version uint32 + +// Flag indicates TPM verisions that are supported by the application. +type Flag uint32 + +// CommandPriority is used to determine which pending command to submit whenever the TPM is free. +type CommandPriority uint32 + +// Command parameters: +// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.10240.0/shared/tbs.h +const ( + // https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/ns-tbs-tdtbs_context_params2 + // OR flags to use multiple. + RequestRaw Flag = 1 << iota // Add flag to request raw context + IncludeTPM12 // Add flag to support TPM 1.2 + IncludeTPM20 // Add flag to support TPM 2 + + TPMVersion12 Version = 1 // For TPM 1.2 applications + TPMVersion20 Version = 2 // For TPM 2 applications or applications using multiple TPM versions + + // https://docs.microsoft.com/en-us/windows/desktop/tbs/command-scheduling + // https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsip_submit_command#parameters + LowPriority CommandPriority = 100 // For low priority application use + NormalPriority CommandPriority = 200 // For normal priority application use + HighPriority CommandPriority = 300 // For high priority application use + SystemPriority CommandPriority = 400 // For system tasks that access the TPM + + commandLocalityZero uint32 = 0 // Windows currently only supports TBS_COMMAND_LOCALITY_ZERO. +) + +// Error is the return type of all functions in this package. +type Error uint32 + +func (err Error) Error() string { + if description, ok := errorDescriptions[err]; ok { + return fmt.Sprintf("TBS Error 0x%X: %s", uint32(err), description) + } + return fmt.Sprintf("Unrecognized TBS Error 0x%X", uint32(err)) +} + +func getError(err uintptr) error { + // tbs.dll uses 0x0 as the return value for success. + if err == 0 { + return nil + } + return Error(err) +} + +// TBS Return Codes: +// https://docs.microsoft.com/en-us/windows/desktop/TBS/tbs-return-codes +const ( + ErrInternalError Error = 0x80284001 + ErrBadParameter Error = 0x80284002 + ErrInvalidOutputPointer Error = 0x80284003 + ErrInvalidContext Error = 0x80284004 + ErrInsufficientBuffer Error = 0x80284005 + ErrIOError Error = 0x80284006 + ErrInvalidContextParam Error = 0x80284007 + ErrServiceNotRunning Error = 0x80284008 + ErrTooManyTBSContexts Error = 0x80284009 + ErrTooManyResources Error = 0x8028400A + ErrServiceStartPending Error = 0x8028400B + ErrPPINotSupported Error = 0x8028400C + ErrCommandCanceled Error = 0x8028400D + ErrBufferTooLarge Error = 0x8028400E + ErrTPMNotFound Error = 0x8028400F + ErrServiceDisabled Error = 0x80284010 + ErrNoEventLog Error = 0x80284011 + ErrAccessDenied Error = 0x80284012 + ErrProvisioningNotAllowed Error = 0x80284013 + ErrPPIFunctionUnsupported Error = 0x80284014 + ErrOwnerauthNotFound Error = 0x80284015 +) + +var errorDescriptions = map[Error]string{ + ErrInternalError: "An internal software error occurred.", + ErrBadParameter: "One or more parameter values are not valid.", + ErrInvalidOutputPointer: "A specified output pointer is bad.", + ErrInvalidContext: "The specified context handle does not refer to a valid context.", + ErrInsufficientBuffer: "The specified output buffer is too small.", + ErrIOError: "An error occurred while communicating with the TPM.", + ErrInvalidContextParam: "A context parameter that is not valid was passed when attempting to create a TBS context.", + ErrServiceNotRunning: "The TBS service is not running and could not be started.", + ErrTooManyTBSContexts: "A new context could not be created because there are too many open contexts.", + ErrTooManyResources: "A new virtual resource could not be created because there are too many open virtual resources.", + ErrServiceStartPending: "The TBS service has been started but is not yet running.", + ErrPPINotSupported: "The physical presence interface is not supported.", + ErrCommandCanceled: "The command was canceled.", + ErrBufferTooLarge: "The input or output buffer is too large.", + ErrTPMNotFound: "A compatible Trusted Platform Module (TPM) Security Device cannot be found on this computer.", + ErrServiceDisabled: "The TBS service has been disabled.", + ErrNoEventLog: "The TBS event log is not available.", + ErrAccessDenied: "The caller does not have the appropriate rights to perform the requested operation.", + ErrProvisioningNotAllowed: "The TPM provisioning action is not allowed by the specified flags.", + ErrPPIFunctionUnsupported: "The Physical Presence Interface of this firmware does not support the requested method.", + ErrOwnerauthNotFound: "The requested TPM OwnerAuth value was not found.", +} + +// Tbs.dll provides an API for making calls to the TPM: +// https://docs.microsoft.com/en-us/windows/desktop/TBS/tpm-base-services-portal +var ( + tbsDLL = syscall.NewLazyDLL("Tbs.dll") + tbsGetDeviceInfo = tbsDLL.NewProc("Tbsi_GetDeviceInfo") + tbsCreateContext = tbsDLL.NewProc("Tbsi_Context_Create") + tbsContextClose = tbsDLL.NewProc("Tbsip_Context_Close") + tbsSubmitCommand = tbsDLL.NewProc("Tbsip_Submit_Command") + tbsGetTCGLog = tbsDLL.NewProc("Tbsi_Get_TCG_Log") +) + +// Returns the address of the beginning of a slice or 0 for a nil slice. +func sliceAddress(s []byte) uintptr { + if len(s) == 0 { + return 0 + } + return uintptr(unsafe.Pointer(&(s[0]))) +} + +// Declaration of TPM_DEVICE_INFO from tbs.h +type DeviceInfo struct { + StructVersion uint32 + TPMVersion Version + TPMInterfaceType uint32 + TPMImpRevision uint32 +} + +func GetDeviceInfo() (*DeviceInfo, error) { + info := DeviceInfo{} + // TBS_RESULT Tbsi_GetDeviceInfo( + // UINT32 Size, + // PVOID Info + // ); + result, _, _ := tbsGetDeviceInfo.Call( + unsafe.Sizeof(info), + uintptr(unsafe.Pointer(&info)), + ) + return &info, getError(result) +} + +// CreateContext creates a new TPM context: +// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsi_context_create +func CreateContext(version Version, flag Flag) (Context, error) { + var context Context + params := struct { + Version + Flag + }{version, flag} + // TBS_RESULT Tbsi_Context_Create( + // _In_ PCTBS_CONTEXT_PARAMS pContextParams, + // _Out_ PTBS_HCONTEXT *phContext + // ); + result, _, _ := tbsCreateContext.Call( + uintptr(unsafe.Pointer(¶ms)), + uintptr(unsafe.Pointer(&context)), + ) + return context, getError(result) +} + +// Close closes an existing TPM context: +// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsip_context_close +func (context Context) Close() error { + // TBS_RESULT Tbsip_Context_Close( + // _In_ TBS_HCONTEXT hContext + // ); + result, _, _ := tbsContextClose.Call(uintptr(context)) + return getError(result) +} + +// SubmitCommand sends commandBuffer to the TPM, returning the number of bytes +// written to responseBuffer. ErrInsufficientBuffer is returned if the +// responseBuffer is too short. ErrInvalidOutputPointer is returned if the +// responseBuffer is nil. On failure, the returned length is unspecified. +// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsip_submit_command +func (context Context) SubmitCommand( + priority CommandPriority, + commandBuffer []byte, + responseBuffer []byte, +) (uint32, error) { + responseBufferLen := uint32(len(responseBuffer)) + + // TBS_RESULT Tbsip_Submit_Command( + // _In_ TBS_HCONTEXT hContext, + // _In_ TBS_COMMAND_LOCALITY Locality, + // _In_ TBS_COMMAND_PRIORITY Priority, + // _In_ const PCBYTE *pabCommand, + // _In_ UINT32 cbCommand, + // _Out_ PBYTE *pabResult, + // _Inout_ UINT32 *pcbOutput + // ); + result, _, _ := tbsSubmitCommand.Call( + uintptr(context), + uintptr(commandLocalityZero), + uintptr(priority), + sliceAddress(commandBuffer), + uintptr(len(commandBuffer)), + sliceAddress(responseBuffer), + uintptr(unsafe.Pointer(&responseBufferLen)), + ) + return responseBufferLen, getError(result) +} + +// GetTCGLog gets the system event log, returning the number of bytes written +// to logBuffer. If logBuffer is nil, the size of the TCG log is returned. +// ErrInsufficientBuffer is returned if the logBuffer is too short. On failure, +// the returned length is unspecified. +// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsi_get_tcg_log +func (context Context) GetTCGLog(logBuffer []byte) (uint32, error) { + logBufferLen := uint32(len(logBuffer)) + + // TBS_RESULT Tbsi_Get_TCG_Log( + // TBS_HCONTEXT hContext, + // PBYTE pOutputBuf, + // PUINT32 pOutputBufLen + // ); + result, _, _ := tbsGetTCGLog.Call( + uintptr(context), + sliceAddress(logBuffer), + uintptr(unsafe.Pointer(&logBufferLen)), + ) + return logBufferLen, getError(result) +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/tbs/tbs_windows_test.go b/vendor/github.com/google/go-tpm/tpmutil/tbs/tbs_windows_test.go new file mode 100644 index 0000000..d67342c --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/tbs/tbs_windows_test.go @@ -0,0 +1,113 @@ +package tbs + +import ( + "bytes" + "os" + "testing" +) + +// Encodes a call to Getrandom() with a buffer of length zero, making this +// command an effective no-op. +var getRandomRawCommand = []byte{128, 1, 0, 0, 0, 12, 0, 0, 1, 123, 0, 0} + +func getContext(t *testing.T) Context { + ctx, err := CreateContext(TPMVersion20, IncludeTPM12|IncludeTPM20) + if err != nil { + t.Skipf("Skipping test as we couldn't access the TPM: %v", err) + } + return ctx +} + +// Get the log by passing in progressively larger buffers +func TestGetLogLargeBuffer(t *testing.T) { + ctx := getContext(t) + defer ctx.Close() + + log := make([]byte, os.Getpagesize()) + for { + logLen, err := ctx.GetTCGLog(log) + if err == nil { + if logLen == 0 { + t.Fatal("Expected positive TCGLog length") + } + return + } + if err != ErrInsufficientBuffer { + t.Fatalf("GetTCGLog failed: %v", err) + } + log = make([]byte, 2*len(log)) + } +} + +// Get the log by passing in nil, checking the size, and then getting the log. +func TestGetLogWithNilSlice(t *testing.T) { + ctx := getContext(t) + defer ctx.Close() + + logLen, err := ctx.GetTCGLog(nil) + if err != nil { + t.Fatalf("First GetTCGLog failed: %v", err) + } + if logLen == 0 { + t.Fatal("Expected positive TCGLog length") + } + + log := make([]byte, logLen) + if _, err := ctx.GetTCGLog(log); err != nil { + t.Fatalf("Second GetTCGLog failed: %v", err) + } +} + +// SubmitCommand can handle a nil command buffer. +func TestSubmitCommandNilCommand(t *testing.T) { + ctx := getContext(t) + defer ctx.Close() + + rawResponse := make([]byte, os.Getpagesize()) + _, err := ctx.SubmitCommand(NormalPriority, nil, rawResponse) + if err != ErrBadParameter { + t.Fatalf("SubmitCommand failed with %v: expected ErrBadParameter", err) + } +} + +// SubmitCommand can handle a nil response buffer. +func TestSubmitCommandNilResponse(t *testing.T) { + ctx := getContext(t) + defer ctx.Close() + + _, err := ctx.SubmitCommand(NormalPriority, getRandomRawCommand, nil) + if err != ErrInvalidOutputPointer { + t.Fatalf("SubmitCommand failed with %v: expected ErrInvalidOutputPointer", err) + } +} + +// SubmitCommand can handle a response buffer that is shorter than necessary. +func TestSubmitCommandShortResponse(t *testing.T) { + ctx := getContext(t) + defer ctx.Close() + + rawResponse := make([]byte, 1) + _, err := ctx.SubmitCommand(NormalPriority, getRandomRawCommand, rawResponse) + if err != ErrInsufficientBuffer { + t.Fatalf("SubmitCommand failed with %v: expected ErrInsufficientBuffer", err) + } +} + +// SubmitCommand can handle a response buffer that is longer than necessary. +func TestSubmitCommandLongResponse(t *testing.T) { + ctx := getContext(t) + defer ctx.Close() + + rawResponse := make([]byte, os.Getpagesize()) + responseLen, err := ctx.SubmitCommand(NormalPriority, getRandomRawCommand, rawResponse) + if err != nil { + t.Fatalf("SubmitCommand failed: %v", err) + } + rawResponse = rawResponse[:responseLen] + + // Expected response buffer for getRandomRawCommand + expectedGetRandomRawResponse := []byte{128, 1, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0} + if !bytes.Equal(rawResponse, expectedGetRandomRawResponse) { + t.Fatalf("Got response of %v, expected %v", rawResponse, expectedGetRandomRawResponse) + } +} diff --git a/vendor/github.com/rekby/gpt/gpt.go b/vendor/github.com/rekby/gpt/gpt.go index 8306c39..17f65b7 100644 --- a/vendor/github.com/rekby/gpt/gpt.go +++ b/vendor/github.com/rekby/gpt/gpt.go @@ -334,81 +334,8 @@ func (this Table) Write(writer io.WriteSeeker) (err error) { return } -////////////////////////////////////////////// -//////////////// INTERNALS /////////////////// -////////////////////////////////////////////// - -// Multiply two int64 numbers with overflow check -// Algorithm from https://gist.github.com/areed/85d3614a58400e417027 -func mul(a, b int64) (res int64, ok bool) { - const mostPositive = 1<<63 - 1 - const mostNegative = -(mostPositive + 1) - - if a == 0 || b == 0 || a == 1 || b == 1 { - return a * b, true - } - if a == mostNegative || b == mostNegative { - return a * b, false - } - c := a * b - return c, c/b == a -} - -func guidToString(byteGuid [16]byte) string { - byteToChars := func(b byte) (res []byte) { - res = make([]byte, 0, 2) - for i := 1; i >= 0; i-- { - switch b >> uint(4*i) & 0x0F { - case 0: - res = append(res, '0') - case 1: - res = append(res, '1') - case 2: - res = append(res, '2') - case 3: - res = append(res, '3') - case 4: - res = append(res, '4') - case 5: - res = append(res, '5') - case 6: - res = append(res, '6') - case 7: - res = append(res, '7') - case 8: - res = append(res, '8') - case 9: - res = append(res, '9') - case 10: - res = append(res, 'A') - case 11: - res = append(res, 'B') - case 12: - res = append(res, 'C') - case 13: - res = append(res, 'D') - case 14: - res = append(res, 'E') - case 15: - res = append(res, 'F') - } - } - return - } - s := make([]byte, 0, 36) - byteOrder := [...]int{3, 2, 1, 0, -1, 5, 4, -1, 7, 6, -1, 8, 9, -1, 10, 11, 12, 13, 14, 15} - for _, i := range byteOrder { - if i == -1 { - s = append(s, '-') - } else { - s = append(s, byteToChars(byteGuid[i])...) - } - } - return string(s) -} - // Use for create guid predefined values in snippet http://play.golang.org/p/uOd_WQtiwE -func stringToGuid(guid string) (res [16]byte, err error) { +func StringToGuid(guid string) (res [16]byte, err error) { byteOrder := [...]int{3, 2, 1, 0, -1, 5, 4, -1, 7, 6, -1, 8, 9, -1, 10, 11, 12, 13, 14, 15} if len(guid) != 36 { err = fmt.Errorf("BAD guid string length.") @@ -473,7 +400,7 @@ func stringToGuid(guid string) (res [16]byte, err error) { case 'F', 'f': bt |= 15 << shift default: - err = fmt.Errorf("BAD guid char: ", i+pos, ch) + err = fmt.Errorf("BAD guid char at pos %d: '%c'", i+pos, ch) return } } @@ -482,3 +409,76 @@ func stringToGuid(guid string) (res [16]byte, err error) { } return res, nil } + +////////////////////////////////////////////// +//////////////// INTERNALS /////////////////// +////////////////////////////////////////////// + +// Multiply two int64 numbers with overflow check +// Algorithm from https://gist.github.com/areed/85d3614a58400e417027 +func mul(a, b int64) (res int64, ok bool) { + const mostPositive = 1<<63 - 1 + const mostNegative = -(mostPositive + 1) + + if a == 0 || b == 0 || a == 1 || b == 1 { + return a * b, true + } + if a == mostNegative || b == mostNegative { + return a * b, false + } + c := a * b + return c, c/b == a +} + +func guidToString(byteGuid [16]byte) string { + byteToChars := func(b byte) (res []byte) { + res = make([]byte, 0, 2) + for i := 1; i >= 0; i-- { + switch b >> uint(4*i) & 0x0F { + case 0: + res = append(res, '0') + case 1: + res = append(res, '1') + case 2: + res = append(res, '2') + case 3: + res = append(res, '3') + case 4: + res = append(res, '4') + case 5: + res = append(res, '5') + case 6: + res = append(res, '6') + case 7: + res = append(res, '7') + case 8: + res = append(res, '8') + case 9: + res = append(res, '9') + case 10: + res = append(res, 'A') + case 11: + res = append(res, 'B') + case 12: + res = append(res, 'C') + case 13: + res = append(res, 'D') + case 14: + res = append(res, 'E') + case 15: + res = append(res, 'F') + } + } + return + } + s := make([]byte, 0, 36) + byteOrder := [...]int{3, 2, 1, 0, -1, 5, 4, -1, 7, 6, -1, 8, 9, -1, 10, 11, 12, 13, 14, 15} + for _, i := range byteOrder { + if i == -1 { + s = append(s, '-') + } else { + s = append(s, byteToChars(byteGuid[i])...) + } + } + return string(s) +} diff --git a/vendor/github.com/rekby/gpt/gpt_test.go b/vendor/github.com/rekby/gpt/gpt_test.go index 75a1689..60d0850 100644 --- a/vendor/github.com/rekby/gpt/gpt_test.go +++ b/vendor/github.com/rekby/gpt/gpt_test.go @@ -30,7 +30,7 @@ func (this *randomWriteBuffer) Seek(offset int64, whence int) (newOffset int64, return int64(this.offset), nil } -func (this*randomWriteBuffer) Write(p []byte) (n int, err error){ +func (this *randomWriteBuffer) Write(p []byte) (n int, err error) { needLen := this.offset + len(p) if needLen > len(this.buf) { newBuf := make([]byte, needLen) @@ -42,7 +42,6 @@ func (this*randomWriteBuffer) Write(p []byte) (n int, err error){ return len(p), nil } - func TestHeaderRead(t *testing.T) { reader := bytes.NewReader(GPT_TEST_HEADER) h, err := readHeader(reader, 512) @@ -195,7 +194,7 @@ func TestReadWriteTable(t *testing.T) { } } -func TestTableCreateOtherSide(t *testing.T){ +func TestTableCreateOtherSide(t *testing.T) { buf := make([]byte, 512+512+32*512) copy(buf[512:], GPT_TEST_HEADER) copy(buf[1024:], GPT_TEST_ENTRIES) @@ -237,7 +236,7 @@ func TestTableCreateOtherSide(t *testing.T){ if t2.Header.DiskGUID != t1.Header.DiskGUID { t.Error("disk guid") } - if t2.Header.PartitionsTableStartLBA != t2.Header.LastUsableLBA + 1{ + if t2.Header.PartitionsTableStartLBA != t2.Header.LastUsableLBA+1 { t.Error("partitions table start") } if t2.Header.PartitionsArrLen != t1.Header.PartitionsArrLen { @@ -253,7 +252,7 @@ func TestTableCreateOtherSide(t *testing.T){ t.Error("trailing bytes aren't equal") } - if len(t1.Partitions) != len(t2.Partitions){ + if len(t1.Partitions) != len(t2.Partitions) { t.Fatal("partitions len are different") } @@ -285,7 +284,7 @@ func TestTableCreateOtherSide(t *testing.T){ } } -func TestTableCopy(t *testing.T){ +func TestTableCopy(t *testing.T) { buf := make([]byte, 512+512+32*512) copy(buf[512:], GPT_TEST_HEADER) copy(buf[1024:], GPT_TEST_ENTRIES) @@ -346,11 +345,11 @@ func TestTableCopy(t *testing.T){ t.Error("trailing bytes aren't equal") } t1.Header.TrailingBytes[0] += 1 - if t1.Header.TrailingBytes[0] == t2.Header.TrailingBytes[0]{ + if t1.Header.TrailingBytes[0] == t2.Header.TrailingBytes[0] { t.Error("same trailing bytes") } - if len(t1.Partitions) != len(t2.Partitions){ + if len(t1.Partitions) != len(t2.Partitions) { t.Fatal("partitions len are different") } @@ -386,7 +385,7 @@ func TestTableCopy(t *testing.T){ } } -func TestTableNewSize(t *testing.T){ +func TestTableNewSize(t *testing.T) { buf := make([]byte, 512+512+32*512) copy(buf[512:], GPT_TEST_HEADER) copy(buf[1024:], GPT_TEST_ENTRIES) @@ -447,11 +446,11 @@ func TestTableNewSize(t *testing.T){ t.Error("trailing bytes aren't equal") } t1.Header.TrailingBytes[0] += 1 - if t1.Header.TrailingBytes[0] == t2.Header.TrailingBytes[0]{ + if t1.Header.TrailingBytes[0] == t2.Header.TrailingBytes[0] { t.Error("same trailing bytes") } - if len(t1.Partitions) != len(t2.Partitions){ + if len(t1.Partitions) != len(t2.Partitions) { t.Fatal("partitions len are different") } @@ -496,33 +495,32 @@ func TestTableNewSize(t *testing.T){ } } - -func TestGuidToString(t *testing.T){ +func TestGuidToString(t *testing.T) { guid := [...]byte{40, 115, 42, 193, 31, 248, 210, 17, 186, 75, 0, 160, 201, 62, 201, 59} guidS := guidToString(guid) if guidS != "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" { - t.Errorf("Error guid: %v != %v", guidS, "C12A7328-F81F-11D2-BA4B-00A0C93EC93B") + t.Errorf("Error guid: %v != %v", guidS, "C12A7328-F81F-11D2-BA4B-00A0C93EC93B") } } -func TestStringToGuid(t *testing.T){ - guid, err := stringToGuid( "C12A7328-F81F-11D2-BA4B-00A0C93EC93B") +func TestStringToGuid(t *testing.T) { + guid, err := StringToGuid("C12A7328-F81F-11D2-BA4B-00A0C93EC93B") if err != nil { t.Error(err) } if guid != [...]byte{40, 115, 42, 193, 31, 248, 210, 17, 186, 75, 0, 160, 201, 62, 201, 59} { - t.Errorf("Bad result. Expected:\n%v\nResult:\n%v", [...]byte{40, 115, 42, 193, 31, 248, 210, 17, 186, 75, 0, 160, 201, 62, 201, 59}, guid ) + t.Errorf("Bad result. Expected:\n%v\nResult:\n%v", [...]byte{40, 115, 42, 193, 31, 248, 210, 17, 186, 75, 0, 160, 201, 62, 201, 59}, guid) } - if _, err := stringToGuid(""); err == nil { + if _, err := StringToGuid(""); err == nil { t.Error("Must return error") } - if _, err := stringToGuid("C12A7328-F81F-11D2-BA4B-00A0C93EC93BA"); err == nil { + if _, err := StringToGuid("C12A7328-F81F-11D2-BA4B-00A0C93EC93BA"); err == nil { t.Error("Must return error") } - if _, err := stringToGuid("C12A7328-F81F-11D2-BA4B!00A0C93EC93B"); err == nil { + if _, err := StringToGuid("C12A7328-F81F-11D2-BA4B!00A0C93EC93B"); err == nil { t.Error("Must return error") } - if _, err := stringToGuid("C12A7328-F81F-11D2-BA4B-00A0C93EC93Z"); err == nil { + if _, err := StringToGuid("C12A7328-F81F-11D2-BA4B-00A0C93EC93Z"); err == nil { t.Error("Must return error") } -} \ No newline at end of file +} diff --git a/vendor/github.com/stretchr/testify/.travis.yml b/vendor/github.com/stretchr/testify/.travis.yml index f408b4c..da6ba0d 100644 --- a/vendor/github.com/stretchr/testify/.travis.yml +++ b/vendor/github.com/stretchr/testify/.travis.yml @@ -2,14 +2,18 @@ language: go sudo: false -go: - - "1.8" - - "1.9" - - "1.10" - - tip - -script: - - ./.travis.gogenerate.sh - - ./.travis.gofmt.sh - - ./.travis.govet.sh - - go test -v -race $(go list ./... | grep -v vendor) +matrix: + include: + - go: "1.8.x" + - go: "1.9.x" + - go: "1.10.x" + - go: "1.11.x" + env: GO111MODULE=off + - go: "1.11.x" + env: GO111MODULE=on + - go: tip + script: + - ./.travis.gogenerate.sh + - ./.travis.gofmt.sh + - ./.travis.govet.sh + - go test -v -race $(go list ./... | grep -v vendor) diff --git a/vendor/github.com/stretchr/testify/LICENSE b/vendor/github.com/stretchr/testify/LICENSE index 473b670..f38ec59 100644 --- a/vendor/github.com/stretchr/testify/LICENSE +++ b/vendor/github.com/stretchr/testify/LICENSE @@ -1,22 +1,21 @@ -Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +MIT License -Please consider promoting this project if you find it useful. +Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of the Software, -and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT -OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/stretchr/testify/README.md b/vendor/github.com/stretchr/testify/README.md index 51b6df3..ef0197e 100644 --- a/vendor/github.com/stretchr/testify/README.md +++ b/vendor/github.com/stretchr/testify/README.md @@ -287,8 +287,10 @@ To install Testify, use `go get`: This will then make the following packages available to you: github.com/stretchr/testify/assert + github.com/stretchr/testify/require github.com/stretchr/testify/mock - github.com/stretchr/testify/http + github.com/stretchr/testify/suite + github.com/stretchr/testify/http (deprecated) Import the `testify/assert` package into your code using this template: @@ -319,7 +321,7 @@ To update Testify to the latest version, use `go get -u github.com/stretchr/test Supported go versions ================== -We support the three major Go versions, which are 1.8, 1.9 and 1.10 at the moment. +We support the three major Go versions, which are 1.9, 1.10, and 1.11 at the moment. ------ @@ -329,3 +331,12 @@ Contributing Please feel free to submit issues, fork the repository and send pull requests! When submitting an issue, we ask that you please include a complete test function that demonstrates the issue. Extra credit for those using Testify to write the test code that demonstrates it. + +Code generation is used. Look for `CODE GENERATED AUTOMATICALLY` at the top of some files. Run `go generate ./...` to update generated files. + +------ + +License +======= + +This project is licensed under the terms of the MIT license. diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index aa1c2b9..e0364e9 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -113,6 +113,17 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { return Error(t, err, append([]interface{}{msg}, args...)...) } +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) +} + // Exactlyf asserts that two objects are equal in value and type. // // assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) @@ -157,6 +168,31 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool return FileExists(t, path, append([]interface{}{msg}, args...)...) } +// Greaterf asserts that the first element is greater than the second +// +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1)) +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Greater(t, e1, e2, append([]interface{}{msg}, args...)...) +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return GreaterOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) +} + // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // @@ -289,6 +325,14 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) } +// YAMLEqf asserts that two YAML strings are equivalent. +func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // @@ -300,6 +344,31 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf return Len(t, object, length, append([]interface{}{msg}, args...)...) } +// Lessf asserts that the first element is less than the second +// +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2)) +// assert.Lessf(t, "a", "b", "error message %s", "formatted") +func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Less(t, e1, e2, append([]interface{}{msg}, args...)...) +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) +} + // Nilf asserts that the specified object is nil. // // assert.Nilf(t, err, "error message %s", "formatted") @@ -444,6 +513,19 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in return Regexp(t, rx, str, append([]interface{}{msg}, args...)...) } +// Samef asserts that two pointers reference the same object. +// +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Same(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index de39f79..2683040 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -215,6 +215,28 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { return Errorf(a.t, err, msg, args...) } +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Eventually(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Eventuallyf(a.t, condition, waitFor, tick, msg, args...) +} + // Exactly asserts that two objects are equal in value and type. // // a.Exactly(int32(123), int64(123)) @@ -303,6 +325,56 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) b return FileExistsf(a.t, path, msg, args...) } +// Greater asserts that the first element is greater than the second +// +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") +func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Greater(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") +func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return GreaterOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return GreaterOrEqualf(a.t, e1, e2, msg, args...) +} + +// Greaterf asserts that the first element is greater than the second +// +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1)) +// a.Greaterf("b", "a", "error message %s", "formatted") +func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Greaterf(a.t, e1, e2, msg, args...) +} + // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // @@ -567,6 +639,22 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. return JSONEqf(a.t, expected, actual, msg, args...) } +// YAMLEq asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return YAMLEq(a.t, expected, actual, msgAndArgs...) +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return YAMLEqf(a.t, expected, actual, msg, args...) +} + // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // @@ -589,6 +677,56 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in return Lenf(a.t, object, length, msg, args...) } +// Less asserts that the first element is less than the second +// +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") +func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Less(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") +func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return LessOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return LessOrEqualf(a.t, e1, e2, msg, args...) +} + +// Lessf asserts that the first element is less than the second +// +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1, "error message %s", "formatted"), float64(2)) +// a.Lessf("a", "b", "error message %s", "formatted") +func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Lessf(a.t, e1, e2, msg, args...) +} + // Nil asserts that the specified object is nil. // // a.Nil(err) @@ -877,6 +1015,32 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . return Regexpf(a.t, rx, str, msg, args...) } +// Same asserts that two pointers reference the same object. +// +// a.Same(ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Same(a.t, expected, actual, msgAndArgs...) +} + +// Samef asserts that two pointers reference the same object. +// +// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Samef(a.t, expected, actual, msg, args...) +} + // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go new file mode 100644 index 0000000..15a486c --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -0,0 +1,309 @@ +package assert + +import ( + "fmt" + "reflect" +) + +func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { + switch kind { + case reflect.Int: + { + intobj1 := obj1.(int) + intobj2 := obj2.(int) + if intobj1 > intobj2 { + return -1, true + } + if intobj1 == intobj2 { + return 0, true + } + if intobj1 < intobj2 { + return 1, true + } + } + case reflect.Int8: + { + int8obj1 := obj1.(int8) + int8obj2 := obj2.(int8) + if int8obj1 > int8obj2 { + return -1, true + } + if int8obj1 == int8obj2 { + return 0, true + } + if int8obj1 < int8obj2 { + return 1, true + } + } + case reflect.Int16: + { + int16obj1 := obj1.(int16) + int16obj2 := obj2.(int16) + if int16obj1 > int16obj2 { + return -1, true + } + if int16obj1 == int16obj2 { + return 0, true + } + if int16obj1 < int16obj2 { + return 1, true + } + } + case reflect.Int32: + { + int32obj1 := obj1.(int32) + int32obj2 := obj2.(int32) + if int32obj1 > int32obj2 { + return -1, true + } + if int32obj1 == int32obj2 { + return 0, true + } + if int32obj1 < int32obj2 { + return 1, true + } + } + case reflect.Int64: + { + int64obj1 := obj1.(int64) + int64obj2 := obj2.(int64) + if int64obj1 > int64obj2 { + return -1, true + } + if int64obj1 == int64obj2 { + return 0, true + } + if int64obj1 < int64obj2 { + return 1, true + } + } + case reflect.Uint: + { + uintobj1 := obj1.(uint) + uintobj2 := obj2.(uint) + if uintobj1 > uintobj2 { + return -1, true + } + if uintobj1 == uintobj2 { + return 0, true + } + if uintobj1 < uintobj2 { + return 1, true + } + } + case reflect.Uint8: + { + uint8obj1 := obj1.(uint8) + uint8obj2 := obj2.(uint8) + if uint8obj1 > uint8obj2 { + return -1, true + } + if uint8obj1 == uint8obj2 { + return 0, true + } + if uint8obj1 < uint8obj2 { + return 1, true + } + } + case reflect.Uint16: + { + uint16obj1 := obj1.(uint16) + uint16obj2 := obj2.(uint16) + if uint16obj1 > uint16obj2 { + return -1, true + } + if uint16obj1 == uint16obj2 { + return 0, true + } + if uint16obj1 < uint16obj2 { + return 1, true + } + } + case reflect.Uint32: + { + uint32obj1 := obj1.(uint32) + uint32obj2 := obj2.(uint32) + if uint32obj1 > uint32obj2 { + return -1, true + } + if uint32obj1 == uint32obj2 { + return 0, true + } + if uint32obj1 < uint32obj2 { + return 1, true + } + } + case reflect.Uint64: + { + uint64obj1 := obj1.(uint64) + uint64obj2 := obj2.(uint64) + if uint64obj1 > uint64obj2 { + return -1, true + } + if uint64obj1 == uint64obj2 { + return 0, true + } + if uint64obj1 < uint64obj2 { + return 1, true + } + } + case reflect.Float32: + { + float32obj1 := obj1.(float32) + float32obj2 := obj2.(float32) + if float32obj1 > float32obj2 { + return -1, true + } + if float32obj1 == float32obj2 { + return 0, true + } + if float32obj1 < float32obj2 { + return 1, true + } + } + case reflect.Float64: + { + float64obj1 := obj1.(float64) + float64obj2 := obj2.(float64) + if float64obj1 > float64obj2 { + return -1, true + } + if float64obj1 == float64obj2 { + return 0, true + } + if float64obj1 < float64obj2 { + return 1, true + } + } + case reflect.String: + { + stringobj1 := obj1.(string) + stringobj2 := obj2.(string) + if stringobj1 > stringobj2 { + return -1, true + } + if stringobj1 == stringobj2 { + return 0, true + } + if stringobj1 < stringobj2 { + return 1, true + } + } + } + + return 0, false +} + +// Greater asserts that the first element is greater than the second +// +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") +func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + e1Kind := reflect.ValueOf(e1).Kind() + e2Kind := reflect.ValueOf(e2).Kind() + if e1Kind != e2Kind { + return Fail(t, "Elements should be the same type", msgAndArgs...) + } + + res, isComparable := compare(e1, e2, e1Kind) + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + } + + if res != -1 { + return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...) + } + + return true +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") +func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + e1Kind := reflect.ValueOf(e1).Kind() + e2Kind := reflect.ValueOf(e2).Kind() + if e1Kind != e2Kind { + return Fail(t, "Elements should be the same type", msgAndArgs...) + } + + res, isComparable := compare(e1, e2, e1Kind) + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + } + + if res != -1 && res != 0 { + return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...) + } + + return true +} + +// Less asserts that the first element is less than the second +// +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") +func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + e1Kind := reflect.ValueOf(e1).Kind() + e2Kind := reflect.ValueOf(e2).Kind() + if e1Kind != e2Kind { + return Fail(t, "Elements should be the same type", msgAndArgs...) + } + + res, isComparable := compare(e1, e2, e1Kind) + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + } + + if res != 1 { + return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...) + } + + return true +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") +func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + e1Kind := reflect.ValueOf(e1).Kind() + e2Kind := reflect.ValueOf(e2).Kind() + if e1Kind != e2Kind { + return Fail(t, "Elements should be the same type", msgAndArgs...) + } + + res, isComparable := compare(e1, e2, e1Kind) + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + } + + if res != 1 && res != 0 { + return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...) + } + + return true +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order_test.go b/vendor/github.com/stretchr/testify/assert/assertion_order_test.go new file mode 100644 index 0000000..4dd897a --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_order_test.go @@ -0,0 +1,118 @@ +package assert + +import ( + "reflect" + "testing" +) + +func TestCompare(t *testing.T) { + for _, currCase := range []struct { + less interface{} + greater interface{} + cType string + }{ + {less: "a", greater: "b", cType: "string"}, + {less: int(1), greater: int(2), cType: "int"}, + {less: int8(1), greater: int8(2), cType: "int8"}, + {less: int16(1), greater: int16(2), cType: "int16"}, + {less: int32(1), greater: int32(2), cType: "int32"}, + {less: int64(1), greater: int64(2), cType: "int64"}, + {less: uint8(1), greater: uint8(2), cType: "uint8"}, + {less: uint16(1), greater: uint16(2), cType: "uint16"}, + {less: uint32(1), greater: uint32(2), cType: "uint32"}, + {less: uint64(1), greater: uint64(2), cType: "uint64"}, + {less: float32(1), greater: float32(2), cType: "float32"}, + {less: float64(1), greater: float64(2), cType: "float64"}, + } { + resLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind()) + if !isComparable { + t.Error("object should be comparable for type " + currCase.cType) + } + + if resLess != 1 { + t.Errorf("object less should be less than greater for type " + currCase.cType) + } + + resGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind()) + if !isComparable { + t.Error("object are comparable for type " + currCase.cType) + } + + if resGreater != -1 { + t.Errorf("object greater should be greater than less for type " + currCase.cType) + } + + resEqual, isComparable := compare(currCase.less, currCase.less, reflect.ValueOf(currCase.less).Kind()) + if !isComparable { + t.Error("object are comparable for type " + currCase.cType) + } + + if resEqual != 0 { + t.Errorf("objects should be equal for type " + currCase.cType) + } + } +} + +func TestGreater(t *testing.T) { + mockT := new(testing.T) + + if !Greater(mockT, 2, 1) { + t.Error("Greater should return true") + } + + if Greater(mockT, 1, 1) { + t.Error("Greater should return false") + } + + if Greater(mockT, 1, 2) { + t.Error("Greater should return false") + } +} + +func TestGreaterOrEqual(t *testing.T) { + mockT := new(testing.T) + + if !GreaterOrEqual(mockT, 2, 1) { + t.Error("Greater should return true") + } + + if !GreaterOrEqual(mockT, 1, 1) { + t.Error("Greater should return true") + } + + if GreaterOrEqual(mockT, 1, 2) { + t.Error("Greater should return false") + } +} + +func TestLess(t *testing.T) { + mockT := new(testing.T) + + if !Less(mockT, 1, 2) { + t.Error("Less should return true") + } + + if Less(mockT, 1, 1) { + t.Error("Less should return false") + } + + if Less(mockT, 2, 1) { + t.Error("Less should return false") + } +} + +func TestLessOrEqual(t *testing.T) { + mockT := new(testing.T) + + if !LessOrEqual(mockT, 1, 2) { + t.Error("Greater should return true") + } + + if !LessOrEqual(mockT, 1, 1) { + t.Error("Greater should return true") + } + + if LessOrEqual(mockT, 2, 1) { + t.Error("Greater should return false") + } +} diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 5bdec56..044da8b 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -18,6 +18,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/pmezard/go-difflib/difflib" + yaml "gopkg.in/yaml.v2" ) //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl @@ -39,7 +40,7 @@ type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool // for table driven tests. type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool -// ValuesAssertionFunc is a common function prototype when validating an error value. Can be useful +// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful // for table driven tests. type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool @@ -179,7 +180,11 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { return "" } if len(msgAndArgs) == 1 { - return msgAndArgs[0].(string) + msg := msgAndArgs[0] + if msgAsStr, ok := msg.(string); ok { + return msgAsStr + } + return fmt.Sprintf("%+v", msg) } if len(msgAndArgs) > 1 { return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) @@ -346,6 +351,37 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) } +// Same asserts that two pointers reference the same object. +// +// assert.Same(t, ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + expectedPtr, actualPtr := reflect.ValueOf(expected), reflect.ValueOf(actual) + if expectedPtr.Kind() != reflect.Ptr || actualPtr.Kind() != reflect.Ptr { + return Fail(t, "Invalid operation: both arguments must be pointers", msgAndArgs...) + } + + expectedType, actualType := reflect.TypeOf(expected), reflect.TypeOf(actual) + if expectedType != actualType { + return Fail(t, fmt.Sprintf("Pointer expected to be of type %v, but was %v", + expectedType, actualType), msgAndArgs...) + } + + if expected != actual { + return Fail(t, fmt.Sprintf("Not same: \n"+ + "expected: %p %#v\n"+ + "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) + } + + return true +} + // formatUnequalValues takes two values of arbitrary types and returns string // representations appropriate to be presented to the user. // @@ -415,6 +451,17 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return Fail(t, "Expected value not to be nil.", msgAndArgs...) } +// containsKind checks if a specified kind in the slice of kinds. +func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool { + for i := 0; i < len(kinds); i++ { + if kind == kinds[i] { + return true + } + } + + return false +} + // isNil checks if a specified object is nil or not, without Failing. func isNil(object interface{}) bool { if object == nil { @@ -423,7 +470,14 @@ func isNil(object interface{}) bool { value := reflect.ValueOf(object) kind := value.Kind() - if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() { + isNilableKind := containsKind( + []reflect.Kind{ + reflect.Chan, reflect.Func, + reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice}, + kind) + + if isNilableKind && value.IsNil() { return true } @@ -457,14 +511,14 @@ func isEmpty(object interface{}) bool { // collection types are empty when they have no element case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: return objValue.Len() == 0 - // pointers are empty if nil or if the value they point to is empty + // pointers are empty if nil or if the value they point to is empty case reflect.Ptr: if objValue.IsNil() { return true } deref := objValue.Elem().Interface() return isEmpty(deref) - // for all other types, compare against the zero value + // for all other types, compare against the zero value default: zero := reflect.Zero(objValue.Type()) return reflect.DeepEqual(object, zero.Interface()) @@ -607,7 +661,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ func includeElement(list interface{}, element interface{}) (ok, found bool) { listValue := reflect.ValueOf(list) - elementValue := reflect.ValueOf(element) + listKind := reflect.TypeOf(list).Kind() defer func() { if e := recover(); e != nil { ok = false @@ -615,11 +669,12 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) { } }() - if reflect.TypeOf(list).Kind() == reflect.String { + if listKind == reflect.String { + elementValue := reflect.ValueOf(element) return true, strings.Contains(listValue.String(), elementValue.String()) } - if reflect.TypeOf(list).Kind() == reflect.Map { + if listKind == reflect.Map { mapKeys := listValue.MapKeys() for i := 0; i < len(mapKeys); i++ { if ObjectsAreEqual(mapKeys[i].Interface(), element) { @@ -1315,6 +1370,24 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) } +// YAMLEq asserts that two YAML strings are equivalent. +func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + var expectedYAMLAsInterface, actualYAMLAsInterface interface{} + + if err := yaml.Unmarshal([]byte(expected), &expectedYAMLAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...) + } + + if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...) + } + + return Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, msgAndArgs...) +} + func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { t := reflect.TypeOf(v) k := t.Kind() @@ -1327,7 +1400,7 @@ func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { } // diff returns a diff of both values as long as both are of the same type and -// are a struct, map, slice or array. Otherwise it returns an empty string. +// are a struct, map, slice, array or string. Otherwise it returns an empty string. func diff(expected interface{}, actual interface{}) string { if expected == nil || actual == nil { return "" @@ -1345,12 +1418,12 @@ func diff(expected interface{}, actual interface{}) string { } var e, a string - if ek != reflect.String { + if et != reflect.TypeOf("") { e = spewConfig.Sdump(expected) a = spewConfig.Sdump(actual) } else { - e = expected.(string) - a = actual.(string) + e = reflect.ValueOf(expected).String() + a = reflect.ValueOf(actual).String() } diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ @@ -1392,3 +1465,34 @@ var spewConfig = spew.ConfigState{ type tHelper interface { Helper() } + +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + timer := time.NewTimer(waitFor) + ticker := time.NewTicker(tick) + checkPassed := make(chan bool) + defer timer.Stop() + defer ticker.Stop() + defer close(checkPassed) + for { + select { + case <-timer.C: + return Fail(t, "Condition never satisfied", msgAndArgs...) + case result := <-checkPassed: + if result { + return true + } + case <-ticker.C: + go func() { + checkPassed <- condition() + }() + } + } +} diff --git a/vendor/github.com/stretchr/testify/assert/assertions_test.go b/vendor/github.com/stretchr/testify/assert/assertions_test.go index 91b5ee9..a4a9585 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions_test.go +++ b/vendor/github.com/stretchr/testify/assert/assertions_test.go @@ -175,6 +175,8 @@ func TestIsType(t *testing.T) { } +type myType string + func TestEqual(t *testing.T) { mockT := new(testing.T) @@ -200,6 +202,9 @@ func TestEqual(t *testing.T) { if !Equal(mockT, uint64(123), uint64(123)) { t.Error("Equal should return true") } + if !Equal(mockT, myType("1"), myType("1")) { + t.Error("Equal should return true") + } if !Equal(mockT, &struct{}{}, &struct{}{}) { t.Error("Equal should return true (pointer equality is based on equality of underlying value)") } @@ -207,6 +212,32 @@ func TestEqual(t *testing.T) { if Equal(mockT, m["bar"], "something") { t.Error("Equal should return false") } + if Equal(mockT, myType("1"), myType("2")) { + t.Error("Equal should return false") + } +} + +func TestSame(t *testing.T) { + + mockT := new(testing.T) + + ptr := func(i int) *int { + return &i + } + + if Same(mockT, ptr(1), ptr(1)) { + t.Error("Same should return false") + } + if Same(mockT, 1, 1) { + t.Error("Same should return false") + } + p := ptr(2) + if Same(mockT, p, *p) { + t.Error("Same should return false") + } + if !Same(mockT, p, p) { + t.Error("Same should return true") + } } // bufferT implements TestingT. Its implementation of Errorf writes the output that would be produced by @@ -275,6 +306,8 @@ func TestEqualFormatting(t *testing.T) { }{ {equalWant: "want", equalGot: "got", want: "\tassertions.go:\\d+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n"}, {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{"hello, %v!", "world"}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+hello, world!\n"}, + {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{123}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+123\n"}, + {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{struct{ a string }{"hello"}}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+{a:hello}\n"}, } { mockT := &bufferT{} Equal(mockT, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...) @@ -1410,6 +1443,81 @@ func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) { False(t, JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) } +func TestYAMLEq_EqualYAMLString(t *testing.T) { + mockT := new(testing.T) + True(t, YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) +} + +func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { + mockT := new(testing.T) + True(t, YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) +} + +func TestYAMLEq_HashOfArraysAndHashes(t *testing.T) { + mockT := new(testing.T) + expected := ` +numeric: 1.5 +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +` + + actual := ` +numeric: 1.5 +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +` + True(t, YAMLEq(mockT, expected, actual)) +} + +func TestYAMLEq_Array(t *testing.T) { + mockT := new(testing.T) + True(t, YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) +} + +func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(testing.T) + False(t, YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) +} + +func TestYAMLEq_HashesNotEquivalent(t *testing.T) { + mockT := new(testing.T) + False(t, YAMLEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) +} + +func TestYAMLEq_ActualIsSimpleString(t *testing.T) { + mockT := new(testing.T) + False(t, YAMLEq(mockT, `{"foo": "bar"}`, "Simple String")) +} + +func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { + mockT := new(testing.T) + False(t, YAMLEq(mockT, "Simple String", `{"foo": "bar", "hello": "world"}`)) +} + +func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { + mockT := new(testing.T) + True(t, YAMLEq(mockT, "Simple String", "Simple String")) +} + +func TestYAMLEq_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(testing.T) + False(t, YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) +} + func TestDiff(t *testing.T) { expected := ` @@ -1791,3 +1899,25 @@ func TestErrorAssertionFunc(t *testing.T) { }) } } + +func TestEventuallyFalse(t *testing.T) { + mockT := new(testing.T) + + condition := func() bool { + return false + } + + False(t, Eventually(mockT, condition, 100*time.Millisecond, 20*time.Millisecond)) +} + +func TestEventuallyTrue(t *testing.T) { + state := 0 + condition := func() bool { + defer func() { + state = state + 1 + }() + return state == 2 + } + + True(t, Eventually(t, condition, 100*time.Millisecond, 20*time.Millisecond)) +} diff --git a/vendor/github.com/stretchr/testify/assert/forward_assertions_test.go b/vendor/github.com/stretchr/testify/assert/forward_assertions_test.go index 22e1df1..4d83d6a 100644 --- a/vendor/github.com/stretchr/testify/assert/forward_assertions_test.go +++ b/vendor/github.com/stretchr/testify/assert/forward_assertions_test.go @@ -609,3 +609,101 @@ func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { t.Error("JSONEq should return false") } } + +func TestYAMLEqWrapper_EqualYAMLString(t *testing.T) { + assert := New(new(testing.T)) + if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) { + t.Error("YAMLEq should return true") + } + +} + +func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { + assert := New(new(testing.T)) + if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { + t.Error("YAMLEq should return true") + } + +} + +func TestYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) { + assert := New(new(testing.T)) + expected := ` +numeric: 1.5 +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +` + + actual := ` +numeric: 1.5 +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +` + if !assert.YAMLEq(expected, actual) { + t.Error("YAMLEq should return true") + } +} + +func TestYAMLEqWrapper_Array(t *testing.T) { + assert := New(new(testing.T)) + if !assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) { + t.Error("YAMLEq should return true") + } + +} + +func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + assert := New(new(testing.T)) + if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) { + t.Error("YAMLEq should return false") + } +} + +func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { + assert := New(new(testing.T)) + if assert.YAMLEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { + t.Error("YAMLEq should return false") + } +} + +func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { + assert := New(new(testing.T)) + if assert.YAMLEq(`{"foo": "bar"}`, "Simple String") { + t.Error("YAMLEq should return false") + } +} + +func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { + assert := New(new(testing.T)) + if assert.YAMLEq("Simple String", `{"foo": "bar", "hello": "world"}`) { + t.Error("YAMLEq should return false") + } +} + +func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { + assert := New(new(testing.T)) + if !assert.YAMLEq("Simple String", "Simple String") { + t.Error("YAMLEq should return true") + } +} + +func TestYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + assert := New(new(testing.T)) + if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) { + t.Error("YAMLEq should return false") + } +} diff --git a/vendor/github.com/stretchr/testify/go.mod b/vendor/github.com/stretchr/testify/go.mod new file mode 100644 index 0000000..5053648 --- /dev/null +++ b/vendor/github.com/stretchr/testify/go.mod @@ -0,0 +1,8 @@ +module github.com/stretchr/testify + +require ( + github.com/davecgh/go-spew v1.1.0 + github.com/pmezard/go-difflib v1.0.0 + github.com/stretchr/objx v0.1.0 + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/vendor/github.com/stretchr/testify/go.sum b/vendor/github.com/stretchr/testify/go.sum new file mode 100644 index 0000000..cccb647 --- /dev/null +++ b/vendor/github.com/stretchr/testify/go.sum @@ -0,0 +1,9 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go index cc4f642..b5288af 100644 --- a/vendor/github.com/stretchr/testify/mock/mock.go +++ b/vendor/github.com/stretchr/testify/mock/mock.go @@ -176,6 +176,7 @@ func (c *Call) Maybe() *Call { // Mock. // On("MyMethod", 1).Return(nil). // On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) +//go:noinline func (c *Call) On(methodName string, arguments ...interface{}) *Call { return c.Parent.On(methodName, arguments...) } @@ -261,17 +262,21 @@ func (m *Mock) On(methodName string, arguments ...interface{}) *Call { // */ func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) { - for i, call := range m.ExpectedCalls { - if call.Method == method && call.Repeatability > -1 { + var expectedCall *Call + for i, call := range m.ExpectedCalls { + if call.Method == method { _, diffCount := call.Arguments.Diff(arguments) if diffCount == 0 { - return i, call + expectedCall = call + if call.Repeatability > -1 { + return i, call + } } - } } - return -1, nil + + return -1, expectedCall } func (m *Mock) findClosestCall(method string, arguments ...interface{}) (*Call, string) { @@ -343,13 +348,17 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen found, call := m.findExpectedCall(methodName, arguments...) if found < 0 { + // expected call found but it has already been called with repeatable times + if call != nil { + m.mutex.Unlock() + m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(\"%s\").Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo()) + } // we have to fail here - because we don't know what to do // as the return arguments. This is because: // // a) this is a totally unexpected call to this method, // b) the arguments are not what was expected, or // c) the developer has forgotten to add an accompanying On...Return pair. - closestCall, mismatch := m.findClosestCall(methodName, arguments...) m.mutex.Unlock() @@ -691,7 +700,7 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { output = fmt.Sprintf("%s\t%d: PASS: %s matched by %s\n", output, i, actualFmt, matcher) } else { differences++ - output = fmt.Sprintf("%s\t%d: PASS: %s not matched by %s\n", output, i, actualFmt, matcher) + output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher) } } else if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() { diff --git a/vendor/github.com/stretchr/testify/mock/mock_test.go b/vendor/github.com/stretchr/testify/mock/mock_test.go index 978eae2..4fea2fe 100644 --- a/vendor/github.com/stretchr/testify/mock/mock_test.go +++ b/vendor/github.com/stretchr/testify/mock/mock_test.go @@ -32,6 +32,7 @@ func (i *TestExampleImplementation) TheExampleMethod(a, b, c int) (int, error) { return args.Int(0), errors.New("Whoops") } +//go:noinline func (i *TestExampleImplementation) TheExampleMethod2(yesorno bool) { i.Called(yesorno) } @@ -739,6 +740,16 @@ func Test_Mock_findExpectedCall_Respects_Repeatability(t *testing.T) { } } + c = m.On("Once", 1).Return("one").Once() + c.Repeatability = -1 + f, c = m.findExpectedCall("Once", 1) + if assert.Equal(t, -1, f) { + if assert.NotNil(t, c) { + assert.Equal(t, "Once", c.Method) + assert.Equal(t, 1, c.Arguments[0]) + assert.Equal(t, "one", c.ReturnArguments[0]) + } + } } func Test_callString(t *testing.T) { @@ -1492,6 +1503,7 @@ func unexpectedCallRegex(method, calledArg, expectedArg, diff string) string { rMethod, calledArg, rMethod, expectedArg, diff) } +//go:noinline func ConcurrencyTestMethod(m *Mock) { m.Called() } diff --git a/vendor/github.com/stretchr/testify/require/forward_requirements_test.go b/vendor/github.com/stretchr/testify/require/forward_requirements_test.go index b120ae3..1ec65fa 100644 --- a/vendor/github.com/stretchr/testify/require/forward_requirements_test.go +++ b/vendor/github.com/stretchr/testify/require/forward_requirements_test.go @@ -383,3 +383,129 @@ func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { t.Error("Check should fail") } } + +func TestYAMLEqWrapper_EqualYAMLString(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + expected := ` +numeric: 1.5 +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +` + + actual := ` +numeric: 1.5 +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +` + + mockRequire.YAMLEq(expected, actual) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEqWrapper_Array(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`{"foo": "bar"}`, "Simple String") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq("Simple String", `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq("Simple String", "Simple String") + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) + if !mockT.Failed { + t.Error("Check should fail") + } +} diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 535f293..c5903f5 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -14,23 +14,23 @@ import ( // Condition uses a Comparison to assert a complex condition. func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) { - if assert.Condition(t, comp, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Condition(t, comp, msgAndArgs...) { + return + } t.FailNow() } // Conditionf uses a Comparison to assert a complex condition. func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interface{}) { - if assert.Conditionf(t, comp, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Conditionf(t, comp, msg, args...) { + return + } t.FailNow() } @@ -41,12 +41,12 @@ func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interfac // assert.Contains(t, ["Hello", "World"], "World") // assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { - if assert.Contains(t, s, contains, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Contains(t, s, contains, msgAndArgs...) { + return + } t.FailNow() } @@ -57,34 +57,34 @@ func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...int // assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") // assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { - if assert.Containsf(t, s, contains, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Containsf(t, s, contains, msg, args...) { + return + } t.FailNow() } // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { - if assert.DirExists(t, path, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.DirExists(t, path, msgAndArgs...) { + return + } t.FailNow() } // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { - if assert.DirExistsf(t, path, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.DirExistsf(t, path, msg, args...) { + return + } t.FailNow() } @@ -94,12 +94,12 @@ func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { // // assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { - if assert.ElementsMatch(t, listA, listB, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.ElementsMatch(t, listA, listB, msgAndArgs...) { + return + } t.FailNow() } @@ -109,12 +109,12 @@ func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs // // assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { - if assert.ElementsMatchf(t, listA, listB, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.ElementsMatchf(t, listA, listB, msg, args...) { + return + } t.FailNow() } @@ -123,12 +123,12 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // // assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if assert.Empty(t, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Empty(t, object, msgAndArgs...) { + return + } t.FailNow() } @@ -137,12 +137,12 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // // assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { - if assert.Emptyf(t, object, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Emptyf(t, object, msg, args...) { + return + } t.FailNow() } @@ -154,12 +154,12 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if assert.Equal(t, expected, actual, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Equal(t, expected, actual, msgAndArgs...) { + return + } t.FailNow() } @@ -169,12 +169,12 @@ func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...i // actualObj, err := SomeFunction() // assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { - if assert.EqualError(t, theError, errString, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.EqualError(t, theError, errString, msgAndArgs...) { + return + } t.FailNow() } @@ -184,12 +184,12 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // actualObj, err := SomeFunction() // assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { - if assert.EqualErrorf(t, theError, errString, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.EqualErrorf(t, theError, errString, msg, args...) { + return + } t.FailNow() } @@ -198,12 +198,12 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args // // assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if assert.EqualValues(t, expected, actual, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.EqualValues(t, expected, actual, msgAndArgs...) { + return + } t.FailNow() } @@ -212,12 +212,12 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg // // assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if assert.EqualValuesf(t, expected, actual, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.EqualValuesf(t, expected, actual, msg, args...) { + return + } t.FailNow() } @@ -229,12 +229,12 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if assert.Equalf(t, expected, actual, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Equalf(t, expected, actual, msg, args...) { + return + } t.FailNow() } @@ -245,12 +245,12 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // assert.Equal(t, expectedError, err) // } func Error(t TestingT, err error, msgAndArgs ...interface{}) { - if assert.Error(t, err, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Error(t, err, msgAndArgs...) { + return + } t.FailNow() } @@ -261,9 +261,37 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) { // assert.Equal(t, expectedErrorf, err) // } func Errorf(t TestingT, err error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } if assert.Errorf(t, err, msg, args...) { return } + t.FailNow() +} + +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { + return + } if h, ok := t.(tHelper); ok { h.Helper() } @@ -274,12 +302,12 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) { // // assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if assert.Exactly(t, expected, actual, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Exactly(t, expected, actual, msgAndArgs...) { + return + } t.FailNow() } @@ -287,56 +315,56 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // // assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if assert.Exactlyf(t, expected, actual, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Exactlyf(t, expected, actual, msg, args...) { + return + } t.FailNow() } // Fail reports a failure through func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) { - if assert.Fail(t, failureMessage, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Fail(t, failureMessage, msgAndArgs...) { + return + } t.FailNow() } // FailNow fails test func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) { - if assert.FailNow(t, failureMessage, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.FailNow(t, failureMessage, msgAndArgs...) { + return + } t.FailNow() } // FailNowf fails test func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) { - if assert.FailNowf(t, failureMessage, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.FailNowf(t, failureMessage, msg, args...) { + return + } t.FailNow() } // Failf reports a failure through func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { - if assert.Failf(t, failureMessage, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Failf(t, failureMessage, msg, args...) { + return + } t.FailNow() } @@ -344,12 +372,12 @@ func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { // // assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) { - if assert.False(t, value, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.False(t, value, msgAndArgs...) { + return + } t.FailNow() } @@ -357,34 +385,96 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) { // // assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) { - if assert.Falsef(t, value, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Falsef(t, value, msg, args...) { + return + } t.FailNow() } // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { - if assert.FileExists(t, path, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.FileExists(t, path, msgAndArgs...) { + return + } t.FailNow() } // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } if assert.FileExistsf(t, path, msg, args...) { return } + t.FailNow() +} + +// Greater asserts that the first element is greater than the second +// +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") +func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Greater(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") +func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.GreaterOrEqual(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.GreaterOrEqualf(t, e1, e2, msg, args...) { + return + } + t.FailNow() +} + +// Greaterf asserts that the first element is greater than the second +// +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1)) +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Greaterf(t, e1, e2, msg, args...) { + return + } t.FailNow() } @@ -395,12 +485,12 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { - if assert.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) { + return + } t.FailNow() } @@ -411,12 +501,12 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url s // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { - if assert.HTTPBodyContainsf(t, handler, method, url, values, str, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPBodyContainsf(t, handler, method, url, values, str, msg, args...) { + return + } t.FailNow() } @@ -427,12 +517,12 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { - if assert.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) { + return + } t.FailNow() } @@ -443,12 +533,12 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, ur // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { - if assert.HTTPBodyNotContainsf(t, handler, method, url, values, str, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPBodyNotContainsf(t, handler, method, url, values, str, msg, args...) { + return + } t.FailNow() } @@ -458,12 +548,12 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if assert.HTTPError(t, handler, method, url, values, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPError(t, handler, method, url, values, msgAndArgs...) { + return + } t.FailNow() } @@ -473,12 +563,12 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, // // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if assert.HTTPErrorf(t, handler, method, url, values, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPErrorf(t, handler, method, url, values, msg, args...) { + return + } t.FailNow() } @@ -488,12 +578,12 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if assert.HTTPRedirect(t, handler, method, url, values, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPRedirect(t, handler, method, url, values, msgAndArgs...) { + return + } t.FailNow() } @@ -503,12 +593,12 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin // // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if assert.HTTPRedirectf(t, handler, method, url, values, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPRedirectf(t, handler, method, url, values, msg, args...) { + return + } t.FailNow() } @@ -518,12 +608,12 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if assert.HTTPSuccess(t, handler, method, url, values, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPSuccess(t, handler, method, url, values, msgAndArgs...) { + return + } t.FailNow() } @@ -533,12 +623,12 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if assert.HTTPSuccessf(t, handler, method, url, values, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPSuccessf(t, handler, method, url, values, msg, args...) { + return + } t.FailNow() } @@ -546,12 +636,12 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // // assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { - if assert.Implements(t, interfaceObject, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Implements(t, interfaceObject, object, msgAndArgs...) { + return + } t.FailNow() } @@ -559,12 +649,12 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg // // assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { - if assert.Implementsf(t, interfaceObject, object, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Implementsf(t, interfaceObject, object, msg, args...) { + return + } t.FailNow() } @@ -572,56 +662,56 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // // assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if assert.InDelta(t, expected, actual, delta, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDelta(t, expected, actual, delta, msgAndArgs...) { + return + } t.FailNow() } // InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func InDeltaMapValues(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if assert.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) { + return + } t.FailNow() } // InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if assert.InDeltaMapValuesf(t, expected, actual, delta, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDeltaMapValuesf(t, expected, actual, delta, msg, args...) { + return + } t.FailNow() } // InDeltaSlice is the same as InDelta, except it compares two slices. func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { + return + } t.FailNow() } // InDeltaSlicef is the same as InDelta, except it compares two slices. func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if assert.InDeltaSlicef(t, expected, actual, delta, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDeltaSlicef(t, expected, actual, delta, msg, args...) { + return + } t.FailNow() } @@ -629,132 +719,216 @@ func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta f // // assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if assert.InDeltaf(t, expected, actual, delta, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDeltaf(t, expected, actual, delta, msg, args...) { + return + } t.FailNow() } // InEpsilon asserts that expected and actual have a relative error less than epsilon func InEpsilon(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - if assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { + return + } t.FailNow() } // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - if assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { + return + } t.FailNow() } // InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { - if assert.InEpsilonSlicef(t, expected, actual, epsilon, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InEpsilonSlicef(t, expected, actual, epsilon, msg, args...) { + return + } t.FailNow() } // InEpsilonf asserts that expected and actual have a relative error less than epsilon func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { - if assert.InEpsilonf(t, expected, actual, epsilon, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InEpsilonf(t, expected, actual, epsilon, msg, args...) { + return + } t.FailNow() } // IsType asserts that the specified objects are of the same type. func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { - if assert.IsType(t, expectedType, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.IsType(t, expectedType, object, msgAndArgs...) { + return + } t.FailNow() } // IsTypef asserts that the specified objects are of the same type. func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } if assert.IsTypef(t, expectedType, object, msg, args...) { return } + t.FailNow() +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.JSONEq(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.JSONEqf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// YAMLEq asserts that two YAML strings are equivalent. +func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.YAMLEq(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.YAMLEqf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// assert.Len(t, mySlice, 3) +func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Len(t, object, length, msgAndArgs...) { + return + } + t.FailNow() +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Lenf(t, object, length, msg, args...) { + return + } t.FailNow() } -// JSONEq asserts that two JSON strings are equivalent. +// Less asserts that the first element is less than the second // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) -func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { - if assert.JSONEq(t, expected, actual, msgAndArgs...) { - return - } +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") +func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Less(t, e1, e2, msgAndArgs...) { + return + } t.FailNow() } -// JSONEqf asserts that two JSON strings are equivalent. +// LessOrEqual asserts that the first element is less than or equal to the second // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") -func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { - if assert.JSONEqf(t, expected, actual, msg, args...) { - return - } +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") +func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.LessOrEqual(t, e1, e2, msgAndArgs...) { + return + } t.FailNow() } -// Len asserts that the specified object has specific length. -// Len also fails if the object has a type that len() not accept. +// LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.Len(t, mySlice, 3) -func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { - if assert.Len(t, object, length, msgAndArgs...) { - return - } +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.LessOrEqualf(t, e1, e2, msg, args...) { + return + } t.FailNow() } -// Lenf asserts that the specified object has specific length. -// Lenf also fails if the object has a type that len() not accept. +// Lessf asserts that the first element is less than the second // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") -func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { - if assert.Lenf(t, object, length, msg, args...) { - return - } +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2)) +// assert.Lessf(t, "a", "b", "error message %s", "formatted") +func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Lessf(t, e1, e2, msg, args...) { + return + } t.FailNow() } @@ -762,12 +936,12 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // // assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if assert.Nil(t, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Nil(t, object, msgAndArgs...) { + return + } t.FailNow() } @@ -775,12 +949,12 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // // assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { - if assert.Nilf(t, object, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Nilf(t, object, msg, args...) { + return + } t.FailNow() } @@ -791,12 +965,12 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { // assert.Equal(t, expectedObj, actualObj) // } func NoError(t TestingT, err error, msgAndArgs ...interface{}) { - if assert.NoError(t, err, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NoError(t, err, msgAndArgs...) { + return + } t.FailNow() } @@ -807,12 +981,12 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) { // assert.Equal(t, expectedObj, actualObj) // } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { - if assert.NoErrorf(t, err, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NoErrorf(t, err, msg, args...) { + return + } t.FailNow() } @@ -823,12 +997,12 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { // assert.NotContains(t, ["Hello", "World"], "Earth") // assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { - if assert.NotContains(t, s, contains, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotContains(t, s, contains, msgAndArgs...) { + return + } t.FailNow() } @@ -839,12 +1013,12 @@ func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ... // assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") // assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { - if assert.NotContainsf(t, s, contains, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotContainsf(t, s, contains, msg, args...) { + return + } t.FailNow() } @@ -855,12 +1029,12 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // assert.Equal(t, "two", obj[1]) // } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if assert.NotEmpty(t, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotEmpty(t, object, msgAndArgs...) { + return + } t.FailNow() } @@ -871,12 +1045,12 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // assert.Equal(t, "two", obj[1]) // } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { - if assert.NotEmptyf(t, object, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotEmptyf(t, object, msg, args...) { + return + } t.FailNow() } @@ -887,12 +1061,12 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if assert.NotEqual(t, expected, actual, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotEqual(t, expected, actual, msgAndArgs...) { + return + } t.FailNow() } @@ -903,12 +1077,12 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs . // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if assert.NotEqualf(t, expected, actual, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotEqualf(t, expected, actual, msg, args...) { + return + } t.FailNow() } @@ -916,12 +1090,12 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, // // assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if assert.NotNil(t, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotNil(t, object, msgAndArgs...) { + return + } t.FailNow() } @@ -929,12 +1103,12 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // // assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { - if assert.NotNilf(t, object, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotNilf(t, object, msg, args...) { + return + } t.FailNow() } @@ -942,12 +1116,12 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { // // assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if assert.NotPanics(t, f, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotPanics(t, f, msgAndArgs...) { + return + } t.FailNow() } @@ -955,12 +1129,12 @@ func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // // assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { - if assert.NotPanicsf(t, f, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotPanicsf(t, f, msg, args...) { + return + } t.FailNow() } @@ -969,12 +1143,12 @@ func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interfac // assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") // assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { - if assert.NotRegexp(t, rx, str, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotRegexp(t, rx, str, msgAndArgs...) { + return + } t.FailNow() } @@ -983,12 +1157,12 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf // assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { - if assert.NotRegexpf(t, rx, str, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotRegexpf(t, rx, str, msg, args...) { + return + } t.FailNow() } @@ -997,12 +1171,12 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // // assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { - if assert.NotSubset(t, list, subset, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotSubset(t, list, subset, msgAndArgs...) { + return + } t.FailNow() } @@ -1011,34 +1185,34 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i // // assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { - if assert.NotSubsetf(t, list, subset, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotSubsetf(t, list, subset, msg, args...) { + return + } t.FailNow() } // NotZero asserts that i is not the zero value for its type. func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) { - if assert.NotZero(t, i, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotZero(t, i, msgAndArgs...) { + return + } t.FailNow() } // NotZerof asserts that i is not the zero value for its type. func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { - if assert.NotZerof(t, i, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotZerof(t, i, msg, args...) { + return + } t.FailNow() } @@ -1046,12 +1220,12 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { // // assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if assert.Panics(t, f, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Panics(t, f, msgAndArgs...) { + return + } t.FailNow() } @@ -1060,12 +1234,12 @@ func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // // assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if assert.PanicsWithValue(t, expected, f, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.PanicsWithValue(t, expected, f, msgAndArgs...) { + return + } t.FailNow() } @@ -1074,12 +1248,12 @@ func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, m // // assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { - if assert.PanicsWithValuef(t, expected, f, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.PanicsWithValuef(t, expected, f, msg, args...) { + return + } t.FailNow() } @@ -1087,12 +1261,12 @@ func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, // // assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { - if assert.Panicsf(t, f, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Panicsf(t, f, msg, args...) { + return + } t.FailNow() } @@ -1101,12 +1275,12 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} // assert.Regexp(t, regexp.MustCompile("start"), "it's starting") // assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { - if assert.Regexp(t, rx, str, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Regexp(t, rx, str, msgAndArgs...) { + return + } t.FailNow() } @@ -1115,12 +1289,44 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } if assert.Regexpf(t, rx, str, msg, args...) { return } + t.FailNow() +} + +// Same asserts that two pointers reference the same object. +// +// assert.Same(t, ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Same(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// Samef asserts that two pointers reference the same object. +// +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Samef(t, expected, actual, msg, args...) { + return + } t.FailNow() } @@ -1129,12 +1335,12 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // // assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { - if assert.Subset(t, list, subset, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Subset(t, list, subset, msgAndArgs...) { + return + } t.FailNow() } @@ -1143,12 +1349,12 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte // // assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { - if assert.Subsetf(t, list, subset, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Subsetf(t, list, subset, msg, args...) { + return + } t.FailNow() } @@ -1156,12 +1362,12 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // // assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) { - if assert.True(t, value, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.True(t, value, msgAndArgs...) { + return + } t.FailNow() } @@ -1169,12 +1375,12 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) { // // assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) { - if assert.Truef(t, value, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Truef(t, value, msg, args...) { + return + } t.FailNow() } @@ -1182,12 +1388,12 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) { // // assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { - if assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { + return + } t.FailNow() } @@ -1195,33 +1401,33 @@ func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time // // assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { - if assert.WithinDurationf(t, expected, actual, delta, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.WithinDurationf(t, expected, actual, delta, msg, args...) { + return + } t.FailNow() } // Zero asserts that i is the zero value for its type. func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { - if assert.Zero(t, i, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Zero(t, i, msgAndArgs...) { + return + } t.FailNow() } // Zerof asserts that i is the zero value for its type. func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) { - if assert.Zerof(t, i, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Zerof(t, i, msg, args...) { + return + } t.FailNow() } diff --git a/vendor/github.com/stretchr/testify/require/require.go.tmpl b/vendor/github.com/stretchr/testify/require/require.go.tmpl index 6ffc751..55e42dd 100644 --- a/vendor/github.com/stretchr/testify/require/require.go.tmpl +++ b/vendor/github.com/stretchr/testify/require/require.go.tmpl @@ -1,6 +1,6 @@ {{.Comment}} func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { - if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } t.FailNow() } diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index 9fe41db..804fae0 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -216,6 +216,28 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { Errorf(a.t, err, msg, args...) } +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Eventually(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Eventuallyf(a.t, condition, waitFor, tick, msg, args...) +} + // Exactly asserts that two objects are equal in value and type. // // a.Exactly(int32(123), int64(123)) @@ -304,6 +326,56 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { FileExistsf(a.t, path, msg, args...) } +// Greater asserts that the first element is greater than the second +// +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") +func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Greater(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") +func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + GreaterOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + GreaterOrEqualf(a.t, e1, e2, msg, args...) +} + +// Greaterf asserts that the first element is greater than the second +// +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1)) +// a.Greaterf("b", "a", "error message %s", "formatted") +func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Greaterf(a.t, e1, e2, msg, args...) +} + // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // @@ -568,6 +640,22 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. JSONEqf(a.t, expected, actual, msg, args...) } +// YAMLEq asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + YAMLEq(a.t, expected, actual, msgAndArgs...) +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + YAMLEqf(a.t, expected, actual, msg, args...) +} + // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // @@ -590,6 +678,56 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in Lenf(a.t, object, length, msg, args...) } +// Less asserts that the first element is less than the second +// +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") +func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Less(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") +func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + LessOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + LessOrEqualf(a.t, e1, e2, msg, args...) +} + +// Lessf asserts that the first element is less than the second +// +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1, "error message %s", "formatted"), float64(2)) +// a.Lessf("a", "b", "error message %s", "formatted") +func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Lessf(a.t, e1, e2, msg, args...) +} + // Nil asserts that the specified object is nil. // // a.Nil(err) @@ -878,6 +1016,32 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . Regexpf(a.t, rx, str, msg, args...) } +// Same asserts that two pointers reference the same object. +// +// a.Same(ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Same(a.t, expected, actual, msgAndArgs...) +} + +// Samef asserts that two pointers reference the same object. +// +// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Samef(a.t, expected, actual, msg, args...) +} + // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // diff --git a/vendor/github.com/stretchr/testify/require/requirements.go b/vendor/github.com/stretchr/testify/require/requirements.go index 690583a..6b85c5e 100644 --- a/vendor/github.com/stretchr/testify/require/requirements.go +++ b/vendor/github.com/stretchr/testify/require/requirements.go @@ -22,7 +22,7 @@ type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) // for table driven tests. type BoolAssertionFunc func(TestingT, bool, ...interface{}) -// ValuesAssertionFunc is a common function prototype when validating an error value. Can be useful +// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful // for table driven tests. type ErrorAssertionFunc func(TestingT, error, ...interface{}) diff --git a/vendor/github.com/stretchr/testify/require/requirements_test.go b/vendor/github.com/stretchr/testify/require/requirements_test.go index 39467d9..c4d0afa 100644 --- a/vendor/github.com/stretchr/testify/require/requirements_test.go +++ b/vendor/github.com/stretchr/testify/require/requirements_test.go @@ -369,6 +369,111 @@ func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) { } } +func TestYAMLEq_EqualYAMLString(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEq_HashOfArraysAndHashes(t *testing.T) { + mockT := new(MockT) + expected := ` +numeric: 1.5 +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +` + + actual := ` +numeric: 1.5 +hash: + nested: hash + nested_slice: [this, is, nested] +string: "foo" +array: + - foo: bar + - 1 + - "string" + - ["nested", "array", 5.5] +` + YAMLEq(mockT, expected, actual) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEq_Array(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEq_HashesNotEquivalent(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEq_ActualIsSimpleString(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `{"foo": "bar"}`, "Simple String") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, "Simple String", `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, "Simple String", "Simple String") + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestYAMLEq_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(MockT) + YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + func ExampleComparisonAssertionFunc() { t := &testing.T{} // provided by test diff --git a/vendor/github.com/stretchr/testify/suite/suite.go b/vendor/github.com/stretchr/testify/suite/suite.go index e20afbc..d708d7d 100644 --- a/vendor/github.com/stretchr/testify/suite/suite.go +++ b/vendor/github.com/stretchr/testify/suite/suite.go @@ -6,6 +6,7 @@ import ( "os" "reflect" "regexp" + "runtime/debug" "testing" "github.com/stretchr/testify/assert" @@ -55,20 +56,35 @@ func (suite *Suite) Assert() *assert.Assertions { return suite.Assertions } +func failOnPanic(t *testing.T) { + r := recover() + if r != nil { + t.Errorf("test panicked: %v\n%s", r, debug.Stack()) + t.FailNow() + } +} + +// Run provides suite functionality around golang subtests. It should be +// called in place of t.Run(name, func(t *testing.T)) in test suite code. +// The passed-in func will be executed as a subtest with a fresh instance of t. +// Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName. +func (suite *Suite) Run(name string, subtest func()) bool { + oldT := suite.T() + defer suite.SetT(oldT) + return oldT.Run(name, func(t *testing.T) { + suite.SetT(t) + subtest() + }) +} + // Run takes a testing suite and runs all of the tests attached // to it. func Run(t *testing.T, suite TestingSuite) { suite.SetT(t) + defer failOnPanic(t) - if setupAllSuite, ok := suite.(SetupAllSuite); ok { - setupAllSuite.SetupSuite() - } - defer func() { - if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok { - tearDownAllSuite.TearDownSuite() - } - }() - + suiteSetupDone := false + methodFinder := reflect.TypeOf(suite) tests := []testing.InternalTest{} for index := 0; index < methodFinder.NumMethod(); index++ { @@ -78,32 +94,46 @@ func Run(t *testing.T, suite TestingSuite) { fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err) os.Exit(1) } - if ok { - test := testing.InternalTest{ - Name: method.Name, - F: func(t *testing.T) { - parentT := suite.T() - suite.SetT(t) - if setupTestSuite, ok := suite.(SetupTestSuite); ok { - setupTestSuite.SetupTest() + if !ok { + continue + } + if !suiteSetupDone { + if setupAllSuite, ok := suite.(SetupAllSuite); ok { + setupAllSuite.SetupSuite() + } + defer func() { + if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok { + tearDownAllSuite.TearDownSuite() + } + }() + suiteSetupDone = true + } + test := testing.InternalTest{ + Name: method.Name, + F: func(t *testing.T) { + parentT := suite.T() + suite.SetT(t) + defer failOnPanic(t) + + if setupTestSuite, ok := suite.(SetupTestSuite); ok { + setupTestSuite.SetupTest() + } + if beforeTestSuite, ok := suite.(BeforeTest); ok { + beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name) + } + defer func() { + if afterTestSuite, ok := suite.(AfterTest); ok { + afterTestSuite.AfterTest(methodFinder.Elem().Name(), method.Name) } - if beforeTestSuite, ok := suite.(BeforeTest); ok { - beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name) + if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok { + tearDownTestSuite.TearDownTest() } - defer func() { - if afterTestSuite, ok := suite.(AfterTest); ok { - afterTestSuite.AfterTest(methodFinder.Elem().Name(), method.Name) - } - if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok { - tearDownTestSuite.TearDownTest() - } - suite.SetT(parentT) - }() - method.Func.Call([]reflect.Value{reflect.ValueOf(suite)}) - }, - } - tests = append(tests, test) + suite.SetT(parentT) + }() + method.Func.Call([]reflect.Value{reflect.ValueOf(suite)}) + }, } + tests = append(tests, test) } runTests(t, tests) } diff --git a/vendor/github.com/stretchr/testify/suite/suite_test.go b/vendor/github.com/stretchr/testify/suite/suite_test.go index b75fa4a..26dddbd 100644 --- a/vendor/github.com/stretchr/testify/suite/suite_test.go +++ b/vendor/github.com/stretchr/testify/suite/suite_test.go @@ -42,6 +42,99 @@ func (s *SuiteRequireTwice) TestRequireTwo() { r.Equal(1, 2) } +type panickingSuite struct { + Suite + panicInSetupSuite bool + panicInSetupTest bool + panicInBeforeTest bool + panicInTest bool + panicInAfterTest bool + panicInTearDownTest bool + panicInTearDownSuite bool +} + +func (s *panickingSuite) SetupSuite() { + if s.panicInSetupSuite { + panic("oops in setup suite") + } +} + +func (s *panickingSuite) SetupTest() { + if s.panicInSetupTest { + panic("oops in setup test") + } +} + +func (s *panickingSuite) BeforeTest(_, _ string) { + if s.panicInBeforeTest { + panic("oops in before test") + } +} + +func (s *panickingSuite) Test() { + if s.panicInTest { + panic("oops in test") + } +} + +func (s *panickingSuite) AfterTest(_, _ string) { + if s.panicInAfterTest { + panic("oops in after test") + } +} + +func (s *panickingSuite) TearDownTest() { + if s.panicInTearDownTest { + panic("oops in tear down test") + } +} + +func (s *panickingSuite) TearDownSuite() { + if s.panicInTearDownSuite { + panic("oops in tear down suite") + } +} + +func TestSuiteRecoverPanic(t *testing.T) { + ok := true + panickingTests := []testing.InternalTest{ + { + Name: "TestPanicInSetupSuite", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupSuite: true}) }, + }, + { + Name: "TestPanicInSetupTest", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupTest: true}) }, + }, + { + Name: "TestPanicInBeforeTest", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInBeforeTest: true}) }, + }, + { + Name: "TestPanicInTest", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInTest: true}) }, + }, + { + Name: "TestPanicInAfterTest", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInAfterTest: true}) }, + }, + { + Name: "TestPanicInTearDownTest", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownTest: true}) }, + }, + { + Name: "TestPanicInTearDownSuite", + F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownSuite: true}) }, + }, + } + + require.NotPanics(t, func() { + ok = testing.RunTests(allTestsFilter, panickingTests) + }) + + assert.False(t, ok) +} + // This suite is intended to store values to make sure that only // testing-suite-related methods are run. It's also a fully // functional example of a testing suite, using setup/teardown methods @@ -59,6 +152,7 @@ type SuiteTester struct { TearDownTestRunCount int TestOneRunCount int TestTwoRunCount int + TestSubtestRunCount int NonTestMethodRunCount int SuiteNameBefore []string @@ -71,15 +165,6 @@ type SuiteTester struct { TimeAfter []time.Time } -type SuiteSkipTester struct { - // Include our basic suite logic. - Suite - - // Keep counts of how many times each method is run. - SetupSuiteRunCount int - TearDownSuiteRunCount int -} - // The SetupSuite method will be run by testify once, at the very // start of the testing suite, before any tests are run. func (suite *SuiteTester) SetupSuite() { @@ -98,21 +183,12 @@ func (suite *SuiteTester) AfterTest(suiteName, testName string) { suite.TimeAfter = append(suite.TimeAfter, time.Now()) } -func (suite *SuiteSkipTester) SetupSuite() { - suite.SetupSuiteRunCount++ - suite.T().Skip() -} - // The TearDownSuite method will be run by testify once, at the very // end of the testing suite, after all tests have been run. func (suite *SuiteTester) TearDownSuite() { suite.TearDownSuiteRunCount++ } -func (suite *SuiteSkipTester) TearDownSuite() { - suite.TearDownSuiteRunCount++ -} - // The SetupTest method will be run before every test in the suite. func (suite *SuiteTester) SetupTest() { suite.SetupTestRunCount++ @@ -153,6 +229,51 @@ func (suite *SuiteTester) NonTestMethod() { suite.NonTestMethodRunCount++ } +func (suite *SuiteTester) TestSubtest() { + suite.TestSubtestRunCount++ + + for _, t := range []struct { + testName string + }{ + {"first"}, + {"second"}, + } { + suiteT := suite.T() + suite.Run(t.testName, func() { + // We should get a different *testing.T for subtests, so that + // go test recognizes them as proper subtests for output formatting + // and running individual subtests + subTestT := suite.T() + suite.NotEqual(subTestT, suiteT) + }) + suite.Equal(suiteT, suite.T()) + } +} + +type SuiteSkipTester struct { + // Include our basic suite logic. + Suite + + // Keep counts of how many times each method is run. + SetupSuiteRunCount int + TearDownSuiteRunCount int +} + +func (suite *SuiteSkipTester) SetupSuite() { + suite.SetupSuiteRunCount++ + suite.T().Skip() +} + +func (suite *SuiteSkipTester) TestNothing() { + // SetupSuite is only called when at least one test satisfies + // test filter. For this suite to be set up (and then tore down) + // it is necessary to add at least one test method. +} + +func (suite *SuiteSkipTester) TearDownSuite() { + suite.TearDownSuiteRunCount++ +} + // TestRunSuite will be run by the 'go test' command, so within it, we // can run our suite using the Run(*testing.T, TestingSuite) function. func TestRunSuite(t *testing.T) { @@ -168,18 +289,20 @@ func TestRunSuite(t *testing.T) { assert.Equal(t, suiteTester.SetupSuiteRunCount, 1) assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1) - assert.Equal(t, len(suiteTester.SuiteNameAfter), 3) - assert.Equal(t, len(suiteTester.SuiteNameBefore), 3) - assert.Equal(t, len(suiteTester.TestNameAfter), 3) - assert.Equal(t, len(suiteTester.TestNameBefore), 3) + assert.Equal(t, len(suiteTester.SuiteNameAfter), 4) + assert.Equal(t, len(suiteTester.SuiteNameBefore), 4) + assert.Equal(t, len(suiteTester.TestNameAfter), 4) + assert.Equal(t, len(suiteTester.TestNameBefore), 4) assert.Contains(t, suiteTester.TestNameAfter, "TestOne") assert.Contains(t, suiteTester.TestNameAfter, "TestTwo") assert.Contains(t, suiteTester.TestNameAfter, "TestSkip") + assert.Contains(t, suiteTester.TestNameAfter, "TestSubtest") assert.Contains(t, suiteTester.TestNameBefore, "TestOne") assert.Contains(t, suiteTester.TestNameBefore, "TestTwo") assert.Contains(t, suiteTester.TestNameBefore, "TestSkip") + assert.Contains(t, suiteTester.TestNameBefore, "TestSubtest") for _, suiteName := range suiteTester.SuiteNameAfter { assert.Equal(t, "SuiteTester", suiteName) @@ -197,15 +320,16 @@ func TestRunSuite(t *testing.T) { assert.False(t, when.IsZero()) } - // There are three test methods (TestOne, TestTwo, and TestSkip), so + // There are four test methods (TestOne, TestTwo, TestSkip, and TestSubtest), so // the SetupTest and TearDownTest methods (which should be run once for - // each test) should have been run three times. - assert.Equal(t, suiteTester.SetupTestRunCount, 3) - assert.Equal(t, suiteTester.TearDownTestRunCount, 3) + // each test) should have been run four times. + assert.Equal(t, suiteTester.SetupTestRunCount, 4) + assert.Equal(t, suiteTester.TearDownTestRunCount, 4) // Each test should have been run once. assert.Equal(t, suiteTester.TestOneRunCount, 1) assert.Equal(t, suiteTester.TestTwoRunCount, 1) + assert.Equal(t, suiteTester.TestSubtestRunCount, 1) // Methods that don't match the test method identifier shouldn't // have been run at all. @@ -222,6 +346,33 @@ func TestRunSuite(t *testing.T) { } +// This suite has no Test... methods. It's setup and teardown must be skipped. +type SuiteSetupSkipTester struct { + Suite + + setUp bool + toreDown bool +} + +func (s *SuiteSetupSkipTester) SetupSuite() { + s.setUp = true +} + +func (s *SuiteSetupSkipTester) NonTestMethod() { + +} + +func (s *SuiteSetupSkipTester) TearDownSuite() { + s.toreDown = true +} + +func TestSkippingSuiteSetup(t *testing.T) { + suiteTester := new(SuiteSetupSkipTester) + Run(t, suiteTester) + assert.False(t, suiteTester.setUp) + assert.False(t, suiteTester.toreDown) +} + func TestSuiteGetters(t *testing.T) { suite := new(SuiteTester) suite.SetT(t) diff --git a/vendor/github.com/systemboot/systemboot/.gitignore b/vendor/github.com/systemboot/systemboot/.gitignore index 31e1935..8e1f0b6 100644 --- a/vendor/github.com/systemboot/systemboot/.gitignore +++ b/vendor/github.com/systemboot/systemboot/.gitignore @@ -1,4 +1,9 @@ .*.swp localboot/localboot +localboot/.bb netboot/netboot +netboot/.bb +cmds/vpdbootmanager/vpdbootmanager +cmds/fixmynetboot/fixmynetboot uinit/uinit +uinit/.bb diff --git a/vendor/github.com/systemboot/systemboot/.travis.yml b/vendor/github.com/systemboot/systemboot/.travis.yml index 40bfdff..ce81053 100644 --- a/vendor/github.com/systemboot/systemboot/.travis.yml +++ b/vendor/github.com/systemboot/systemboot/.travis.yml @@ -1,18 +1,27 @@ language: go +services: + - docker + +env: + - TEST_SUITE=units + - TEST_SUITE=integration + sudo: false go: - - "1.8" - - "1.9" - - "1.10" - - tip - -before_install: - - go get -t -v ./... + - "1.12" -script: - - ./.travis/tests.sh +script: | + set -x + case $TEST_SUITE in + integration) + docker build -t systemboottest -f .travis/docker/Dockerfile . && + docker run --rm -i systemboottest;; + *) + go get -t -v ./... && + ./.travis/tests.sh + esac after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/systemboot/systemboot/.travis/docker/Dockerfile b/vendor/github.com/systemboot/systemboot/.travis/docker/Dockerfile new file mode 100644 index 0000000..c0bddee --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/.travis/docker/Dockerfile @@ -0,0 +1,21 @@ +# {docker build -t systemboottest -f Dockerfile ../..} +FROM yarikk/systemboot-test-image + +COPY . /go/src/github.com/systemboot/systemboot + +RUN set -x; \ + sudo chmod -R a+w /go/src && \ + cd /go/src/github.com/systemboot/systemboot && \ + go get -v ./... && \ + u-root -build=bb core uinit localboot # netboot + +CMD ./qemu-system-x86_64 \ + -M q35 \ + -L pc-bios/ `# for vga option rom` \ + -kernel bzImage -initrd /tmp/initramfs.linux_amd64.cpio \ + -m 1024 \ + -nographic \ + -append 'console=ttyS0 earlyprintk=ttyS0' \ + -object 'rng-random,filename=/dev/urandom,id=rng0' \ + -device 'virtio-rng-pci,rng=rng0' \ + -hda disk.img diff --git a/vendor/github.com/systemboot/systemboot/.travis/docker/Dockerfile.base b/vendor/github.com/systemboot/systemboot/.travis/docker/Dockerfile.base new file mode 100644 index 0000000..9f3e8b5 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/.travis/docker/Dockerfile.base @@ -0,0 +1,53 @@ +# {docker build -t yarikk/systemboot-test-image -f Dockerfile.base .} +FROM uroottest/test-image-amd64:v3.2.4 + +# Install dependencies +RUN sudo apt-get update && \ + sudo apt-get install -y --no-install-recommends \ + `# tools for creating bootable disk images` \ + gdisk \ + e2fsprogs \ + qemu-utils \ + && \ + sudo rm -rf /var/lib/apt/lists/* + +# Get u-root +RUN go get github.com/u-root/u-root + +# Get Linux kernel +# +# Config taken from: +# curl -s https://raw.githubusercontent.com/linuxboot/demo/master/20190203-FOSDEM-barberio-hendricks/config/linux-config | +# sed \ +# -e '/^# CONFIG_RELOCATABLE / s!.*!CONFIG_RELOCATABLE=y!' `# for kexec` \ +# -e '/^CONFIG_INITRAMFS_SOURCE=/ s!^!#!' \ +# > linux-config +# +COPY linux-config . +RUN set -x; \ + git clone -q --depth 1 -b v4.19 https://github.com/torvalds/linux.git && \ + mv linux-config linux/.config && \ + (cd linux/ && exec make -j$(nproc)) && \ + cp linux/arch/x86/boot/bzImage bzImage && \ + rm -r linux/ + +# Create a bootable disk image to test localboot; the init there simply shuts down. +RUN set -x; \ + mkdir rootfs && \ + cp bzImage rootfs/ && \ + u-root -build=bb -o rootfs/ramfs.cpio -initcmd shutdown && \ + xz --check=crc32 --lzma2=dict=512KiB rootfs/ramfs.cpio && \ + { \ + echo menuentry; \ + echo linux bzImage; \ + echo initrd ramfs.cpio.xz; \ + } > rootfs/grub2.cfg && \ + du -a rootfs/ && \ + qemu-img create -f raw disk.img 20m && \ + sgdisk --clear --new 1::-0 --typecode=1:8300 --change-name=1:'Linux root filesystem' \ + disk.img && \ + mkfs.ext2 -F -E 'offset=1048576' -d rootfs/ disk.img 18m && \ + gdisk -l disk.img && \ + qemu-img convert -f raw -O qcow2 disk.img disk.qcow2 && \ + mv disk.qcow2 disk.img && \ + rm -r rootfs/ diff --git a/vendor/github.com/systemboot/systemboot/.travis/docker/linux-config b/vendor/github.com/systemboot/systemboot/.travis/docker/linux-config new file mode 100644 index 0000000..ee0a959 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/.travis/docker/linux-config @@ -0,0 +1,2345 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/x86 4.19.6 Kernel Configuration +# + +# +# Compiler: gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0 +# +CONFIG_CC_IS_GCC=y +CONFIG_GCC_VERSION=70300 +CONFIG_CLANG_VERSION=0 +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y +CONFIG_THREAD_INFO_IN_TASK=y + +# +# General setup +# +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +# CONFIG_COMPILE_TEST is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_BUILD_SALT="" +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_HOSTNAME="linuxboot" +CONFIG_SWAP=y +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_CROSS_MEMORY_ATTACH is not set +# CONFIG_USELIB is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_ARCH_AUDITSYSCALL=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR=y +CONFIG_GENERIC_IRQ_RESERVATION_MODE=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_SPARSE_IRQ=y +CONFIG_CLOCKSOURCE_WATCHDOG=y +CONFIG_ARCH_CLOCKSOURCE_DATA=y +CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y +CONFIG_GENERIC_CMOS_UPDATE=y + +# +# Timers subsystem +# +CONFIG_HZ_PERIODIC=y +# CONFIG_NO_HZ_IDLE is not set +# CONFIG_NO_HZ is not set +# CONFIG_HIGH_RES_TIMERS is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set +# CONFIG_IRQ_TIME_ACCOUNTING is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_RCU_EXPERT is not set +CONFIG_SRCU=y +CONFIG_TINY_SRCU=y +CONFIG_BUILD_BIN2C=y +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13 +CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y +CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y +CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y +CONFIG_ARCH_SUPPORTS_INT128=y +# CONFIG_CGROUPS is not set +# CONFIG_NAMESPACES is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +#CONFIG_INITRAMFS_SOURCE="/tmp/initramfs.linux_amd64.cpio.xz" +CONFIG_INITRAMFS_ROOT_UID=0 +CONFIG_INITRAMFS_ROOT_GID=0 +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_RD_XZ=y +CONFIG_RD_LZO=y +CONFIG_RD_LZ4=y +CONFIG_INITRAMFS_COMPRESSION=".gz" +# CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_HAVE_PCSPKR_PLATFORM=y +CONFIG_BPF=y +CONFIG_EXPERT=y +CONFIG_MULTIUSER=y +# CONFIG_SGETMASK_SYSCALL is not set +# CONFIG_SYSFS_SYSCALL is not set +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_FHANDLE is not set +# CONFIG_POSIX_TIMERS is not set +CONFIG_PRINTK=y +CONFIG_PRINTK_NMI=y +# CONFIG_BUG is not set +# CONFIG_PCSPKR_PLATFORM is not set +# CONFIG_BASE_FULL is not set +CONFIG_FUTEX=y +CONFIG_FUTEX_PI=y +CONFIG_EPOLL=y +# CONFIG_SIGNALFD is not set +# CONFIG_TIMERFD is not set +# CONFIG_EVENTFD is not set +# CONFIG_SHMEM is not set +# CONFIG_AIO is not set +# CONFIG_ADVISE_SYSCALLS is not set +# CONFIG_MEMBARRIER is not set +# CONFIG_KALLSYMS is not set +# CONFIG_BPF_SYSCALL is not set +# CONFIG_USERFAULTFD is not set +CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y +# CONFIG_RSEQ is not set +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +# CONFIG_PC104 is not set + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_COMPAT_BRK is not set +# CONFIG_SLAB is not set +# CONFIG_SLUB is not set +CONFIG_SLOB=y +# CONFIG_SLAB_MERGE_DEFAULT is not set +# CONFIG_PROFILING is not set +CONFIG_64BIT=y +CONFIG_X86_64=y +CONFIG_X86=y +CONFIG_INSTRUCTION_DECODER=y +CONFIG_OUTPUT_FORMAT="elf64-x86-64" +CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_MMU=y +CONFIG_ARCH_MMAP_RND_BITS_MIN=28 +CONFIG_ARCH_MMAP_RND_BITS_MAX=32 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16 +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_ARCH_MAY_HAVE_PC_FDC=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ARCH_HAS_CPU_RELAX=y +CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y +CONFIG_ARCH_HAS_FILTER_PGPROT=y +CONFIG_HAVE_SETUP_PER_CPU_AREA=y +CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y +CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +CONFIG_ZONE_DMA32=y +CONFIG_AUDIT_ARCH=y +CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y +CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_PGTABLE_LEVELS=4 +CONFIG_CC_HAS_SANE_STACKPROTECTOR=y + +# +# Processor type and features +# +# CONFIG_ZONE_DMA is not set +# CONFIG_SMP is not set +CONFIG_X86_FEATURE_NAMES=y +CONFIG_X86_MPPARSE=y +# CONFIG_GOLDFISH is not set +# CONFIG_RETPOLINE is not set +# CONFIG_INTEL_RDT is not set +# CONFIG_X86_EXTENDED_PLATFORM is not set +# CONFIG_X86_INTEL_LPSS is not set +# CONFIG_X86_AMD_PLATFORM_DEVICE is not set +# CONFIG_IOSF_MBI is not set +# CONFIG_SCHED_OMIT_FRAME_POINTER is not set +# CONFIG_HYPERVISOR_GUEST is not set +CONFIG_NO_BOOTMEM=y +# CONFIG_MK8 is not set +# CONFIG_MPSC is not set +# CONFIG_MCORE2 is not set +# CONFIG_MATOM is not set +CONFIG_GENERIC_CPU=y +CONFIG_X86_INTERNODE_CACHE_SHIFT=6 +CONFIG_X86_L1_CACHE_SHIFT=6 +CONFIG_X86_TSC=y +CONFIG_X86_CMPXCHG64=y +CONFIG_X86_CMOV=y +CONFIG_X86_MINIMUM_CPU_FAMILY=64 +CONFIG_X86_DEBUGCTLMSR=y +# CONFIG_PROCESSOR_SELECT is not set +CONFIG_CPU_SUP_INTEL=y +CONFIG_CPU_SUP_AMD=y +CONFIG_CPU_SUP_CENTAUR=y +CONFIG_HPET_TIMER=y +# CONFIG_DMI is not set +# CONFIG_GART_IOMMU is not set +# CONFIG_CALGARY_IOMMU is not set +CONFIG_NR_CPUS_RANGE_BEGIN=1 +CONFIG_NR_CPUS_RANGE_END=1 +CONFIG_NR_CPUS_DEFAULT=1 +CONFIG_NR_CPUS=1 +CONFIG_UP_LATE_INIT=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set +# CONFIG_X86_MCE is not set + +# +# Performance monitoring +# +CONFIG_PERF_EVENTS_INTEL_UNCORE=y +CONFIG_PERF_EVENTS_INTEL_RAPL=y +CONFIG_PERF_EVENTS_INTEL_CSTATE=y +# CONFIG_PERF_EVENTS_AMD_POWER is not set +CONFIG_X86_VSYSCALL_EMULATION=y +# CONFIG_I8K is not set +# CONFIG_MICROCODE is not set +# CONFIG_X86_MSR is not set +# CONFIG_X86_CPUID is not set +# CONFIG_X86_5LEVEL is not set +CONFIG_X86_DIRECT_GBPAGES=y +CONFIG_ARCH_HAS_MEM_ENCRYPT=y +# CONFIG_AMD_MEM_ENCRYPT is not set +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SPARSEMEM_DEFAULT=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +# CONFIG_X86_PMEM_LEGACY is not set +# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set +CONFIG_X86_RESERVE_LOW=64 +# CONFIG_MTRR is not set +# CONFIG_ARCH_RANDOM is not set +# CONFIG_X86_SMAP is not set +# CONFIG_X86_INTEL_UMIP is not set +# CONFIG_X86_INTEL_MPX is not set +CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS=y +# CONFIG_EFI is not set +# CONFIG_SECCOMP is not set +# CONFIG_HZ_100 is not set +CONFIG_HZ_250=y +# CONFIG_HZ_300 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=250 +CONFIG_KEXEC=y +CONFIG_KEXEC_FILE=y +CONFIG_ARCH_HAS_KEXEC_PURGATORY=y +# CONFIG_KEXEC_VERIFY_SIG is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_PHYSICAL_START=0x1000000 +CONFIG_RELOCATABLE=y +CONFIG_PHYSICAL_ALIGN=0x200000 +CONFIG_LEGACY_VSYSCALL_EMULATE=y +# CONFIG_LEGACY_VSYSCALL_NONE is not set +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="earlyprintk=serial,ttyS0,57600 console=ttyS0,57600" +# CONFIG_CMDLINE_OVERRIDE is not set +# CONFIG_MODIFY_LDT_SYSCALL is not set +CONFIG_HAVE_LIVEPATCH=y +CONFIG_ARCH_HAS_ADD_PAGES=y +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y +CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y + +# +# Power management and ACPI options +# +# CONFIG_SUSPEND is not set +# CONFIG_HIBERNATION is not set +# CONFIG_PM is not set +CONFIG_ARCH_SUPPORTS_ACPI=y +CONFIG_ACPI=y +CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y +CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y +CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y +# CONFIG_ACPI_DEBUGGER is not set +CONFIG_ACPI_SPCR_TABLE=y +CONFIG_ACPI_LPIT=y +# CONFIG_ACPI_PROCFS_POWER is not set +CONFIG_ACPI_REV_OVERRIDE_POSSIBLE=y +# CONFIG_ACPI_EC_DEBUGFS is not set +CONFIG_ACPI_AC=y +CONFIG_ACPI_BATTERY=y +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_FAN=y +# CONFIG_ACPI_DOCK is not set +CONFIG_ACPI_CPU_FREQ_PSS=y +CONFIG_ACPI_PROCESSOR_CSTATE=y +CONFIG_ACPI_PROCESSOR_IDLE=y +CONFIG_ACPI_PROCESSOR=y +# CONFIG_ACPI_PROCESSOR_AGGREGATOR is not set +CONFIG_ACPI_THERMAL=y +CONFIG_ACPI_CUSTOM_DSDT_FILE="" +CONFIG_ARCH_HAS_ACPI_TABLE_UPGRADE=y +CONFIG_ACPI_TABLE_UPGRADE=y +# CONFIG_ACPI_DEBUG is not set +# CONFIG_ACPI_PCI_SLOT is not set +# CONFIG_ACPI_CONTAINER is not set +CONFIG_ACPI_HOTPLUG_IOAPIC=y +# CONFIG_ACPI_SBS is not set +# CONFIG_ACPI_HED is not set +# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set +# CONFIG_ACPI_NFIT is not set +CONFIG_HAVE_ACPI_APEI=y +CONFIG_HAVE_ACPI_APEI_NMI=y +# CONFIG_ACPI_APEI is not set +# CONFIG_DPTF_POWER is not set +# CONFIG_PMIC_OPREGION is not set +# CONFIG_ACPI_CONFIGFS is not set +CONFIG_X86_PM_TIMER=y +# CONFIG_SFI is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# CPU Idle +# +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +# CONFIG_CPU_IDLE_GOV_MENU is not set +# CONFIG_INTEL_IDLE is not set + +# +# Bus options (PCI etc.) +# +CONFIG_PCI=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_DOMAINS=y +CONFIG_MMCONF_FAM10H=y +# CONFIG_PCI_CNB20LE_QUIRK is not set +# CONFIG_PCIEPORTBUS is not set +# CONFIG_PCI_MSI is not set +CONFIG_PCI_QUIRKS=y +# CONFIG_PCI_DEBUG is not set +# CONFIG_PCI_STUB is not set +CONFIG_PCI_LOCKLESS_CONFIG=y +# CONFIG_PCI_IOV is not set +# CONFIG_PCI_PRI is not set +# CONFIG_PCI_PASID is not set +CONFIG_PCI_LABEL=y +# CONFIG_HOTPLUG_PCI is not set + +# +# PCI controller drivers +# + +# +# Cadence PCIe controllers support +# + +# +# DesignWare PCI Core Support +# + +# +# PCI Endpoint +# +# CONFIG_PCI_ENDPOINT is not set + +# +# PCI switch controller drivers +# +# CONFIG_PCI_SW_SWITCHTEC is not set +# CONFIG_ISA_BUS is not set +CONFIG_ISA_DMA_API=y +CONFIG_AMD_NB=y +# CONFIG_PCCARD is not set +# CONFIG_RAPIDIO is not set +# CONFIG_X86_SYSFB is not set + +# +# Binary Emulations +# +# CONFIG_IA32_EMULATION is not set +# CONFIG_X86_X32 is not set +CONFIG_X86_DEV_DMA_OPS=y +CONFIG_HAVE_GENERIC_GUP=y + +# +# Firmware Drivers +# +# CONFIG_EDD is not set +# CONFIG_FIRMWARE_MEMMAP is not set +# CONFIG_DELL_RBU is not set +# CONFIG_DCDBAS is not set +# CONFIG_ISCSI_IBFT_FIND is not set +# CONFIG_FW_CFG_SYSFS is not set +CONFIG_GOOGLE_FIRMWARE=y +CONFIG_GOOGLE_COREBOOT_TABLE=y +CONFIG_GOOGLE_COREBOOT_TABLE_ACPI=y +CONFIG_GOOGLE_MEMCONSOLE=y +CONFIG_GOOGLE_MEMCONSOLE_COREBOOT=y +CONFIG_GOOGLE_VPD=y + +# +# Tegra firmware driver +# +CONFIG_HAVE_KVM=y +# CONFIG_VIRTUALIZATION is not set + +# +# General architecture-dependent options +# +CONFIG_CRASH_CORE=y +CONFIG_KEXEC_CORE=y +CONFIG_HAVE_OPROFILE=y +CONFIG_OPROFILE_NMI_TIMER=y +# CONFIG_JUMP_LABEL is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_HAVE_IOREMAP_PROT=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_KPROBES_ON_FTRACE=y +CONFIG_HAVE_FUNCTION_ERROR_INJECTION=y +CONFIG_HAVE_NMI=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_ARCH_HAS_FORTIFY_SOURCE=y +CONFIG_ARCH_HAS_SET_MEMORY=y +CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y +CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_RSEQ=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y +CONFIG_HAVE_USER_RETURN_NOTIFIER=y +CONFIG_HAVE_PERF_EVENTS_NMI=y +CONFIG_HAVE_HARDLOCKUP_DETECTOR_PERF=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y +CONFIG_HAVE_CMPXCHG_LOCAL=y +CONFIG_HAVE_CMPXCHG_DOUBLE=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_HAVE_STACKPROTECTOR=y +CONFIG_CC_HAS_STACKPROTECTOR_NONE=y +# CONFIG_STACKPROTECTOR is not set +CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD=y +CONFIG_HAVE_ARCH_HUGE_VMAP=y +CONFIG_HAVE_ARCH_SOFT_DIRTY=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_HAVE_ARCH_MMAP_RND_BITS=y +CONFIG_HAVE_EXIT_THREAD=y +CONFIG_ARCH_MMAP_RND_BITS=28 +CONFIG_HAVE_COPY_THREAD_TLS=y +CONFIG_HAVE_STACK_VALIDATION=y +CONFIG_HAVE_ARCH_VMAP_STACK=y +CONFIG_VMAP_STACK=y +CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y +CONFIG_STRICT_KERNEL_RWX=y +CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y +CONFIG_ARCH_HAS_REFCOUNT=y +# CONFIG_REFCOUNT_FULL is not set +CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y + +# +# GCOV-based kernel profiling +# +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y +CONFIG_PLUGIN_HOSTCC="" +CONFIG_HAVE_GCC_PLUGINS=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=1 +# CONFIG_MODULES is not set +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_BLOCK=y +CONFIG_BLK_SCSI_REQUEST=y +CONFIG_BLK_DEV_BSG=y +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_ZONED is not set +# CONFIG_BLK_CMDLINE_PARSER is not set +# CONFIG_BLK_WBT is not set +# CONFIG_BLK_SED_OPAL is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_EFI_PARTITION=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_MQ_VIRTIO=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_MQ_IOSCHED_DEADLINE=y +CONFIG_MQ_IOSCHED_KYBER=y +# CONFIG_IOSCHED_BFQ is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +CONFIG_INLINE_READ_UNLOCK=y +CONFIG_INLINE_READ_UNLOCK_IRQ=y +CONFIG_INLINE_WRITE_UNLOCK=y +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y +CONFIG_ARCH_USE_QUEUED_RWLOCKS=y +CONFIG_ARCH_HAS_SYNC_CORE_BEFORE_USERMODE=y +CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ELFCORE=y +CONFIG_BINFMT_SCRIPT=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_COREDUMP is not set + +# +# Memory Management options +# +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_SPARSEMEM=y +CONFIG_HAVE_MEMORY_PRESENT=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +# CONFIG_MEMORY_HOTPLUG is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_TRANSPARENT_HUGEPAGE is not set +CONFIG_ARCH_WANTS_THP_SWAP=y +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +# CONFIG_FRONTSWAP is not set +# CONFIG_CMA is not set +# CONFIG_ZPOOL is not set +# CONFIG_ZBUD is not set +# CONFIG_ZSMALLOC is not set +CONFIG_GENERIC_EARLY_IOREMAP=y +# CONFIG_IDLE_PAGE_TRACKING is not set +CONFIG_ARCH_HAS_ZONE_DEVICE=y +CONFIG_ARCH_USES_HIGH_VMA_FLAGS=y +CONFIG_ARCH_HAS_PKEYS=y +# CONFIG_PERCPU_STATS is not set +# CONFIG_GUP_BENCHMARK is not set +CONFIG_ARCH_HAS_PTE_SPECIAL=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +# CONFIG_TLS is not set +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_INTERFACE is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +CONFIG_NET_IP_TUNNEL=y +# CONFIG_SYN_COOKIES is not set +# CONFIG_NET_IPVTI is not set +# CONFIG_NET_FOU is not set +# CONFIG_NET_FOU_IP_TUNNELS is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_INET_UDP_DIAG is not set +# CONFIG_INET_RAW_DIAG is not set +# CONFIG_INET_DIAG_DESTROY is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +# CONFIG_IPV6_VTI is not set +CONFIG_IPV6_SIT=n +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_IPV6_SEG6_LWTUNNEL is not set +# CONFIG_IPV6_SEG6_HMAC is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +# CONFIG_NETFILTER is not set +# CONFIG_BPFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +CONFIG_HAVE_NET_DSA=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_PHONET is not set +# CONFIG_6LOWPAN is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_VSOCKETS is not set +# CONFIG_NETLINK_DIAG is not set +# CONFIG_MPLS is not set +# CONFIG_NET_NSH is not set +# CONFIG_HSR is not set +# CONFIG_NET_SWITCHDEV is not set +# CONFIG_NET_L3_MASTER_DEV is not set +# CONFIG_NET_NCSI is not set +CONFIG_NET_RX_BUSY_POLL=y +CONFIG_BQL=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_AF_KCM is not set +CONFIG_WIRELESS=y +# CONFIG_CFG80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# +CONFIG_MAC80211_STA_HASH_MAX_SIZE=0 +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set +# CONFIG_PSAMPLE is not set +# CONFIG_NET_IFE is not set +# CONFIG_LWTUNNEL is not set +CONFIG_DST_CACHE=y +CONFIG_GRO_CELLS=y +# CONFIG_NET_DEVLINK is not set +CONFIG_MAY_USE_DEVLINK=y +# CONFIG_FAILOVER is not set +CONFIG_HAVE_EBPF_JIT=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_UEVENT_HELPER is not set +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set + +# +# Firmware loader +# +# CONFIG_FW_LOADER is not set +# CONFIG_ALLOW_DEV_COREDUMP is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y + +# +# Bus devices +# +# CONFIG_CONNECTOR is not set +# CONFIG_GNSS is not set +# CONFIG_MTD is not set +# CONFIG_OF is not set +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +# CONFIG_PARPORT is not set +CONFIG_PNP=y +CONFIG_PNP_DEBUG_MESSAGES=y + +# +# Protocols +# +CONFIG_PNPACPI=y +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_NULL_BLK is not set +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_SKD is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_VIRTIO_BLK is not set +# CONFIG_BLK_DEV_RBD is not set +# CONFIG_BLK_DEV_RSXX is not set + +# +# NVME Support +# +# CONFIG_BLK_DEV_NVME is not set +# CONFIG_NVME_FC is not set + +# +# Misc devices +# +# CONFIG_DUMMY_IRQ is not set +# CONFIG_IBM_ASM is not set +# CONFIG_PHANTOM is not set +# CONFIG_SGI_IOC4 is not set +# CONFIG_TIFM_CORE is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_HP_ILO is not set +# CONFIG_SRAM is not set +# CONFIG_PCI_ENDPOINT_TEST is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_CB710_CORE is not set + +# +# Texas Instruments shared transport line discipline +# + +# +# Altera FPGA firmware download module (requires I2C) +# +# CONFIG_INTEL_MEI is not set +# CONFIG_INTEL_MEI_ME is not set +# CONFIG_INTEL_MEI_TXE is not set +# CONFIG_VMWARE_VMCI is not set + +# +# Intel MIC & related support +# + +# +# Intel MIC Bus Driver +# +# CONFIG_INTEL_MIC_BUS is not set + +# +# SCIF Bus Driver +# +# CONFIG_SCIF_BUS is not set + +# +# VOP Bus Driver +# +# CONFIG_VOP_BUS is not set + +# +# Intel MIC Host Driver +# + +# +# Intel MIC Card Driver +# + +# +# SCIF Driver +# + +# +# Intel MIC Coprocessor State Management (COSM) Drivers +# + +# +# VOP Driver +# +# CONFIG_GENWQE is not set +# CONFIG_ECHO is not set +# CONFIG_MISC_RTSX_PCI is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +CONFIG_SCSI_MQ_DEFAULT=y +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_SCSI_CXGB3_ISCSI is not set +# CONFIG_SCSI_CXGB4_ISCSI is not set +# CONFIG_SCSI_BNX2_ISCSI is not set +# CONFIG_BE2ISCSI is not set +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_HPSA is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_3W_SAS is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_AIC94XX is not set +# CONFIG_SCSI_MVSAS is not set +# CONFIG_SCSI_MVUMI is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_ARCMSR is not set +# CONFIG_SCSI_ESAS2R is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_MEGARAID_SAS is not set +# CONFIG_SCSI_MPT3SAS is not set +# CONFIG_SCSI_MPT2SAS is not set +# CONFIG_SCSI_SMARTPQI is not set +# CONFIG_SCSI_UFSHCD is not set +# CONFIG_SCSI_HPTIOP is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_VMWARE_PVSCSI is not set +# CONFIG_SCSI_SNIC is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_ISCI is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_STEX is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_QLA_ISCSI is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_WD719X is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_PMCRAID is not set +# CONFIG_SCSI_PM8001 is not set +# CONFIG_SCSI_VIRTIO is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +CONFIG_ATA=y +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_ATA_ACPI=y +CONFIG_SATA_PMP=y + +# +# Controllers with non-SFF native interface +# +CONFIG_SATA_AHCI=y +CONFIG_SATA_MOBILE_LPM_POLICY=0 +# CONFIG_SATA_AHCI_PLATFORM is not set +# CONFIG_SATA_INIC162X is not set +# CONFIG_SATA_ACARD_AHCI is not set +# CONFIG_SATA_SIL24 is not set +CONFIG_ATA_SFF=y + +# +# SFF controllers with custom DMA interface +# +# CONFIG_PDC_ADMA is not set +# CONFIG_SATA_QSTOR is not set +# CONFIG_SATA_SX4 is not set +CONFIG_ATA_BMDMA=y + +# +# SATA SFF controllers with BMDMA +# +# CONFIG_ATA_PIIX is not set +# CONFIG_SATA_MV is not set +# CONFIG_SATA_NV is not set +# CONFIG_SATA_PROMISE is not set +# CONFIG_SATA_SIL is not set +# CONFIG_SATA_SIS is not set +# CONFIG_SATA_SVW is not set +# CONFIG_SATA_ULI is not set +# CONFIG_SATA_VIA is not set +# CONFIG_SATA_VITESSE is not set + +# +# PATA SFF controllers with BMDMA +# +# CONFIG_PATA_ALI is not set +# CONFIG_PATA_AMD is not set +# CONFIG_PATA_ARTOP is not set +# CONFIG_PATA_ATIIXP is not set +# CONFIG_PATA_ATP867X is not set +# CONFIG_PATA_CMD64X is not set +# CONFIG_PATA_CYPRESS is not set +# CONFIG_PATA_EFAR is not set +# CONFIG_PATA_HPT366 is not set +# CONFIG_PATA_HPT37X is not set +# CONFIG_PATA_HPT3X2N is not set +# CONFIG_PATA_HPT3X3 is not set +# CONFIG_PATA_IT8213 is not set +# CONFIG_PATA_IT821X is not set +# CONFIG_PATA_JMICRON is not set +# CONFIG_PATA_MARVELL is not set +# CONFIG_PATA_NETCELL is not set +# CONFIG_PATA_NINJA32 is not set +# CONFIG_PATA_NS87415 is not set +# CONFIG_PATA_OLDPIIX is not set +# CONFIG_PATA_OPTIDMA is not set +# CONFIG_PATA_PDC2027X is not set +# CONFIG_PATA_PDC_OLD is not set +# CONFIG_PATA_RADISYS is not set +# CONFIG_PATA_RDC is not set +# CONFIG_PATA_SCH is not set +# CONFIG_PATA_SERVERWORKS is not set +# CONFIG_PATA_SIL680 is not set +# CONFIG_PATA_SIS is not set +# CONFIG_PATA_TOSHIBA is not set +# CONFIG_PATA_TRIFLEX is not set +# CONFIG_PATA_VIA is not set +# CONFIG_PATA_WINBOND is not set + +# +# PIO-only SFF controllers +# +# CONFIG_PATA_CMD640_PCI is not set +# CONFIG_PATA_MPIIX is not set +# CONFIG_PATA_NS87410 is not set +# CONFIG_PATA_OPTI is not set +# CONFIG_PATA_PLATFORM is not set +# CONFIG_PATA_RZ1000 is not set + +# +# Generic fallback / legacy drivers +# +# CONFIG_PATA_ACPI is not set +# CONFIG_ATA_GENERIC is not set +# CONFIG_PATA_LEGACY is not set +# CONFIG_MD is not set +# CONFIG_TARGET_CORE is not set +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_FIREWIRE is not set +# CONFIG_FIREWIRE_NOSY is not set +# CONFIG_MACINTOSH_DRIVERS is not set +CONFIG_NETDEVICES=y +CONFIG_MII=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_NET_FC is not set +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_VXLAN is not set +# CONFIG_MACSEC is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_TUN is not set +# CONFIG_TUN_VNET_CROSS_LE is not set +# CONFIG_VETH is not set +# CONFIG_VIRTIO_NET is not set +# CONFIG_NLMON is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# + +# +# Distributed Switch Architecture drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_VORTEX is not set +# CONFIG_TYPHOON is not set +CONFIG_NET_VENDOR_ADAPTEC=y +# CONFIG_ADAPTEC_STARFIRE is not set +CONFIG_NET_VENDOR_AGERE=y +# CONFIG_ET131X is not set +CONFIG_NET_VENDOR_ALACRITECH=y +# CONFIG_SLICOSS is not set +CONFIG_NET_VENDOR_ALTEON=y +# CONFIG_ACENIC is not set +# CONFIG_ALTERA_TSE is not set +CONFIG_NET_VENDOR_AMAZON=y +CONFIG_NET_VENDOR_AMD=y +# CONFIG_AMD8111_ETH is not set +# CONFIG_PCNET32 is not set +# CONFIG_AMD_XGBE is not set +CONFIG_NET_VENDOR_AQUANTIA=y +# CONFIG_AQTION is not set +CONFIG_NET_VENDOR_ARC=y +CONFIG_NET_VENDOR_ATHEROS=y +# CONFIG_ATL2 is not set +# CONFIG_ATL1 is not set +# CONFIG_ATL1E is not set +# CONFIG_ATL1C is not set +# CONFIG_ALX is not set +CONFIG_NET_VENDOR_AURORA=y +# CONFIG_AURORA_NB8800 is not set +CONFIG_NET_VENDOR_BROADCOM=y +# CONFIG_B44 is not set +# CONFIG_BCMGENET is not set +# CONFIG_BNX2 is not set +# CONFIG_CNIC is not set +# CONFIG_TIGON3 is not set +# CONFIG_BNX2X is not set +# CONFIG_SYSTEMPORT is not set +# CONFIG_BNXT is not set +CONFIG_NET_VENDOR_BROCADE=y +# CONFIG_BNA is not set +CONFIG_NET_VENDOR_CADENCE=y +# CONFIG_MACB is not set +CONFIG_NET_VENDOR_CAVIUM=y +# CONFIG_THUNDER_NIC_PF is not set +# CONFIG_THUNDER_NIC_VF is not set +# CONFIG_THUNDER_NIC_BGX is not set +# CONFIG_THUNDER_NIC_RGX is not set +CONFIG_CAVIUM_PTP=y +# CONFIG_LIQUIDIO is not set +CONFIG_NET_VENDOR_CHELSIO=y +# CONFIG_CHELSIO_T1 is not set +# CONFIG_CHELSIO_T3 is not set +# CONFIG_CHELSIO_T4 is not set +# CONFIG_CHELSIO_T4VF is not set +CONFIG_NET_VENDOR_CISCO=y +# CONFIG_ENIC is not set +CONFIG_NET_VENDOR_CORTINA=y +# CONFIG_CX_ECAT is not set +# CONFIG_DNET is not set +CONFIG_NET_VENDOR_DEC=y +# CONFIG_NET_TULIP is not set +CONFIG_NET_VENDOR_DLINK=y +# CONFIG_DL2K is not set +# CONFIG_SUNDANCE is not set +CONFIG_NET_VENDOR_EMULEX=y +# CONFIG_BE2NET is not set +CONFIG_NET_VENDOR_EZCHIP=y +CONFIG_NET_VENDOR_HP=y +# CONFIG_HP100 is not set +CONFIG_NET_VENDOR_HUAWEI=y +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_INTEL=y +CONFIG_E100=y +CONFIG_E1000=y +CONFIG_E1000E=y +CONFIG_E1000E_HWTS=y +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set +# CONFIG_IXGB is not set +# CONFIG_IXGBE is not set +# CONFIG_I40E is not set +# CONFIG_JME is not set +CONFIG_NET_VENDOR_MARVELL=y +# CONFIG_MVMDIO is not set +# CONFIG_SKGE is not set +# CONFIG_SKY2 is not set +CONFIG_NET_VENDOR_MELLANOX=y +# CONFIG_MLX4_EN is not set +# CONFIG_MLX5_CORE is not set +# CONFIG_MLXSW_CORE is not set +# CONFIG_MLXFW is not set +CONFIG_NET_VENDOR_MICREL=y +# CONFIG_KS8851_MLL is not set +# CONFIG_KSZ884X_PCI is not set +CONFIG_NET_VENDOR_MICROSEMI=y +CONFIG_NET_VENDOR_MYRI=y +# CONFIG_MYRI10GE is not set +# CONFIG_FEALNX is not set +CONFIG_NET_VENDOR_NATSEMI=y +# CONFIG_NATSEMI is not set +# CONFIG_NS83820 is not set +CONFIG_NET_VENDOR_NETERION=y +# CONFIG_S2IO is not set +# CONFIG_VXGE is not set +CONFIG_NET_VENDOR_NETRONOME=y +CONFIG_NET_VENDOR_NI=y +CONFIG_NET_VENDOR_8390=y +# CONFIG_NE2K_PCI is not set +CONFIG_NET_VENDOR_NVIDIA=y +# CONFIG_FORCEDETH is not set +CONFIG_NET_VENDOR_OKI=y +# CONFIG_ETHOC is not set +CONFIG_NET_VENDOR_PACKET_ENGINES=y +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +CONFIG_NET_VENDOR_QLOGIC=y +# CONFIG_QLA3XXX is not set +# CONFIG_QLCNIC is not set +# CONFIG_QLGE is not set +# CONFIG_NETXEN_NIC is not set +# CONFIG_QED is not set +CONFIG_NET_VENDOR_QUALCOMM=y +# CONFIG_QCOM_EMAC is not set +# CONFIG_RMNET is not set +CONFIG_NET_VENDOR_RDC=y +# CONFIG_R6040 is not set +CONFIG_NET_VENDOR_REALTEK=y +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_R8169 is not set +CONFIG_NET_VENDOR_RENESAS=y +CONFIG_NET_VENDOR_ROCKER=y +CONFIG_NET_VENDOR_SAMSUNG=y +# CONFIG_SXGBE_ETH is not set +CONFIG_NET_VENDOR_SEEQ=y +CONFIG_NET_VENDOR_SOLARFLARE=y +# CONFIG_SFC is not set +# CONFIG_SFC_FALCON is not set +CONFIG_NET_VENDOR_SILAN=y +# CONFIG_SC92031 is not set +CONFIG_NET_VENDOR_SIS=y +# CONFIG_SIS900 is not set +# CONFIG_SIS190 is not set +CONFIG_NET_VENDOR_SMSC=y +# CONFIG_EPIC100 is not set +# CONFIG_SMSC911X is not set +# CONFIG_SMSC9420 is not set +CONFIG_NET_VENDOR_SOCIONEXT=y +CONFIG_NET_VENDOR_STMICRO=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NET_VENDOR_SUN=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_CASSINI is not set +# CONFIG_NIU is not set +CONFIG_NET_VENDOR_SYNOPSYS=y +# CONFIG_DWC_XLGMAC is not set +CONFIG_NET_VENDOR_TEHUTI=y +# CONFIG_TEHUTI is not set +CONFIG_NET_VENDOR_TI=y +# CONFIG_TI_CPSW_ALE is not set +# CONFIG_TLAN is not set +CONFIG_NET_VENDOR_VIA=y +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set +CONFIG_NET_VENDOR_WIZNET=y +# CONFIG_WIZNET_W5100 is not set +# CONFIG_WIZNET_W5300 is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_NET_SB1000 is not set +# CONFIG_MDIO_DEVICE is not set +# CONFIG_PHYLIB is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Host-side USB support is needed for USB Network Adapter support +# +CONFIG_WLAN=y +# CONFIG_WIRELESS_WDS is not set +CONFIG_WLAN_VENDOR_ADMTEK=y +CONFIG_WLAN_VENDOR_ATH=y +# CONFIG_ATH_DEBUG is not set +# CONFIG_ATH5K_PCI is not set +CONFIG_WLAN_VENDOR_ATMEL=y +CONFIG_WLAN_VENDOR_BROADCOM=y +CONFIG_WLAN_VENDOR_CISCO=y +CONFIG_WLAN_VENDOR_INTEL=y +CONFIG_WLAN_VENDOR_INTERSIL=y +# CONFIG_HOSTAP is not set +# CONFIG_PRISM54 is not set +CONFIG_WLAN_VENDOR_MARVELL=y +CONFIG_WLAN_VENDOR_MEDIATEK=y +CONFIG_WLAN_VENDOR_RALINK=y +CONFIG_WLAN_VENDOR_REALTEK=y +CONFIG_WLAN_VENDOR_RSI=y +CONFIG_WLAN_VENDOR_ST=y +CONFIG_WLAN_VENDOR_TI=y +CONFIG_WLAN_VENDOR_ZYDAS=y +CONFIG_WLAN_VENDOR_QUANTENNA=y + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_VMXNET3 is not set +# CONFIG_FUJITSU_ES is not set +# CONFIG_NET_FAILOVER is not set +# CONFIG_ISDN is not set +# CONFIG_NVM is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set +# CONFIG_INPUT_MATRIXKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_BYD=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_CYPRESS=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +CONFIG_MOUSE_PS2_FOCALTECH=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set +# CONFIG_RMI4_CORE is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_USERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_NOZOMI is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +CONFIG_SERIAL_EARLYCON=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +CONFIG_SERIAL_8250_PNP=y +# CONFIG_SERIAL_8250_FINTEK is not set +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_EXAR=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_DW is not set +# CONFIG_SERIAL_8250_RT288X is not set +CONFIG_SERIAL_8250_LPSS=y +CONFIG_SERIAL_8250_MID=y +# CONFIG_SERIAL_8250_MOXA is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_UARTLITE is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_SERIAL_RP2 is not set +# CONFIG_SERIAL_FSL_LPUART is not set +# CONFIG_SERIAL_DEV_BUS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_VIRTIO_CONSOLE is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +CONFIG_HW_RANDOM_INTEL=y +CONFIG_HW_RANDOM_AMD=y +CONFIG_HW_RANDOM_VIA=y +CONFIG_HW_RANDOM_VIRTIO=y +# CONFIG_NVRAM is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_MWAVE is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_HPET is not set +# CONFIG_HANGCHECK_TIMER is not set +CONFIG_TCG_TPM=y +CONFIG_HW_RANDOM_TPM=y +CONFIG_TCG_TIS_CORE=y +CONFIG_TCG_TIS=y +# CONFIG_TCG_NSC is not set +# CONFIG_TCG_ATMEL is not set +# CONFIG_TCG_INFINEON is not set +# CONFIG_TCG_CRB is not set +# CONFIG_TCG_VTPM_PROXY is not set +# CONFIG_TELCLOCK is not set +CONFIG_DEVPORT=y +# CONFIG_XILLYBUS is not set +# CONFIG_RANDOM_TRUST_CPU is not set + +# +# I2C support +# +# CONFIG_I2C is not set +# CONFIG_SPI is not set +# CONFIG_SPMI is not set +# CONFIG_HSI is not set +# CONFIG_PPS is not set + +# +# PTP clock support +# + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +# CONFIG_PINCTRL is not set +# CONFIG_GPIOLIB is not set +# CONFIG_W1 is not set +# CONFIG_POWER_AVS is not set +# CONFIG_POWER_RESET is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_BQ27XXX is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_HWMON is not set +CONFIG_THERMAL=y +# CONFIG_THERMAL_STATISTICS is not set +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +# CONFIG_THERMAL_WRITABLE_TRIPS is not set +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set +CONFIG_THERMAL_GOV_STEP_WISE=y +# CONFIG_THERMAL_GOV_BANG_BANG is not set +# CONFIG_THERMAL_GOV_USER_SPACE is not set +# CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_EMULATION is not set +# CONFIG_INTEL_POWERCLAMP is not set +# CONFIG_INTEL_SOC_DTS_THERMAL is not set + +# +# ACPI INT340X thermal drivers +# +# CONFIG_INT340X_THERMAL is not set +# CONFIG_INTEL_PCH_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CROS_EC is not set +# CONFIG_MFD_MADERA is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_MFD_INTEL_QUARK_I2C_GPIO is not set +# CONFIG_LPC_ICH is not set +# CONFIG_LPC_SCH is not set +# CONFIG_MFD_INTEL_LPSS_ACPI is not set +# CONFIG_MFD_INTEL_LPSS_PCI is not set +# CONFIG_MFD_JANZ_CMODIO is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_MT6397 is not set +# CONFIG_MFD_RDC321X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_SYSCON is not set +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_VX855 is not set +# CONFIG_REGULATOR is not set +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_AGP is not set +CONFIG_VGA_ARB=y +CONFIG_VGA_ARB_MAX_GPUS=16 +# CONFIG_VGA_SWITCHEROO is not set +# CONFIG_DRM is not set +# CONFIG_DRM_DP_CEC is not set + +# +# ACP (Audio CoProcessor) Configuration +# + +# +# AMD Library routines +# + +# +# Frame buffer Devices +# +# CONFIG_FB is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +# CONFIG_VGACON_SOFT_SCROLLBACK is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_DUMMY_CONSOLE_COLUMNS=80 +CONFIG_DUMMY_CONSOLE_ROWS=25 +# CONFIG_SOUND is not set + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_ACRUX is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_AUREAL is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_COUGAR is not set +# CONFIG_HID_CMEDIA is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_GEMBIRD is not set +# CONFIG_HID_GFRM is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_ITE is not set +# CONFIG_HID_JABRA is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LENOVO is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_MAYFLASH is not set +# CONFIG_HID_REDRAGON is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_NTI is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PLANTRONICS is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEAM is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_RMI is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_UDRAW_PS3 is not set +# CONFIG_HID_XINMO is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HID_SENSOR_HUB is not set +# CONFIG_HID_ALPS is not set + +# +# Intel ISH HID support +# +# CONFIG_INTEL_ISH_HID is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SUPPORT is not set +# CONFIG_UWB is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_INFINIBAND is not set +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +CONFIG_RTC_LIB=y +CONFIG_RTC_MC146818_LIB=y +# CONFIG_RTC_CLASS is not set +# CONFIG_DMADEVICES is not set + +# +# DMABUF options +# +# CONFIG_SYNC_FILE is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +# CONFIG_VIRT_DRIVERS is not set +CONFIG_VIRTIO=y +CONFIG_VIRTIO_MENU=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_PCI_LEGACY=y +# CONFIG_VIRTIO_BALLOON is not set +# CONFIG_VIRTIO_INPUT is not set +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +# CONFIG_X86_PLATFORM_DEVICES is not set +CONFIG_PMC_ATOM=y +# CONFIG_CHROME_PLATFORMS is not set +# CONFIG_MELLANOX_PLATFORM is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Common Clock Framework +# +# CONFIG_HWSPINLOCK is not set + +# +# Clock Source drivers +# +CONFIG_CLKEVT_I8253=y +CONFIG_CLKBLD_I8253=y +# CONFIG_MAILBOX is not set +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers +# +# CONFIG_REMOTEPROC is not set + +# +# Rpmsg drivers +# +# CONFIG_RPMSG_VIRTIO is not set +# CONFIG_SOUNDWIRE is not set + +# +# SOC (System On Chip) specific Drivers +# + +# +# Amlogic SoC drivers +# + +# +# Broadcom SoC drivers +# + +# +# NXP/Freescale QorIQ SoC drivers +# + +# +# i.MX SoC drivers +# + +# +# Qualcomm SoC drivers +# +# CONFIG_SOC_TI is not set + +# +# Xilinx SoC drivers +# +# CONFIG_XILINX_VCU is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_EXTCON is not set +# CONFIG_MEMORY is not set +# CONFIG_IIO is not set +# CONFIG_NTB is not set +# CONFIG_VME_BUS is not set +# CONFIG_PWM is not set + +# +# IRQ chip support +# +CONFIG_ARM_GIC_MAX_NR=1 +# CONFIG_IPACK_BUS is not set +# CONFIG_RESET_CONTROLLER is not set +# CONFIG_FMC is not set + +# +# PHY Subsystem +# +# CONFIG_GENERIC_PHY is not set +# CONFIG_BCM_KONA_USB2_PHY is not set +# CONFIG_PHY_PXA_28NM_HSIC is not set +# CONFIG_PHY_PXA_28NM_USB2 is not set +# CONFIG_POWERCAP is not set +# CONFIG_MCB is not set + +# +# Performance monitor support +# +# CONFIG_RAS is not set +# CONFIG_THUNDERBOLT is not set + +# +# Android +# +# CONFIG_ANDROID is not set +# CONFIG_LIBNVDIMM is not set +# CONFIG_DAX is not set +# CONFIG_NVMEM is not set + +# +# HW tracing support +# +# CONFIG_STM is not set +# CONFIG_INTEL_TH is not set +# CONFIG_FPGA is not set +# CONFIG_UNISYS_VISORBUS is not set +# CONFIG_SIOX is not set +# CONFIG_SLIMBUS is not set + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_FS_IOMAP=y +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT2=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_ENCRYPTION is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_F2FS_FS is not set +# CONFIG_FS_DAX is not set +# CONFIG_EXPORTFS_BLOCK_OPS is not set +# CONFIG_FILE_LOCKING is not set +# CONFIG_FS_ENCRYPTION is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_FUSE_FS is not set +# CONFIG_OVERLAY_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=y + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +# CONFIG_PROC_KCORE is not set +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +# CONFIG_PROC_CHILDREN is not set +CONFIG_KERNFS=y +CONFIG_SYSFS=y +# CONFIG_HUGETLBFS is not set +# CONFIG_CONFIGFS_FS is not set +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +# CONFIG_NLS_UTF8 is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +CONFIG_SECURITYFS=y +CONFIG_PAGE_TABLE_ISOLATION=y +# CONFIG_FORTIFY_SOURCE is not set +# CONFIG_STATIC_USERMODEHELPER is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_AKCIPHER2=y +CONFIG_CRYPTO_KPP2=y +CONFIG_CRYPTO_ACOMP2=y +# CONFIG_CRYPTO_RSA is not set +# CONFIG_CRYPTO_DH is not set +# CONFIG_CRYPTO_ECDH is not set +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_NULL2=y +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_MCRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +CONFIG_CRYPTO_ENGINE=y + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_CHACHA20POLY1305 is not set +# CONFIG_CRYPTO_AEGIS128 is not set +# CONFIG_CRYPTO_AEGIS128L is not set +# CONFIG_CRYPTO_AEGIS256 is not set +# CONFIG_CRYPTO_AEGIS128_AESNI_SSE2 is not set +# CONFIG_CRYPTO_AEGIS128L_AESNI_SSE2 is not set +# CONFIG_CRYPTO_AEGIS256_AESNI_SSE2 is not set +# CONFIG_CRYPTO_MORUS640 is not set +# CONFIG_CRYPTO_MORUS640_SSE2 is not set +# CONFIG_CRYPTO_MORUS1280 is not set +# CONFIG_CRYPTO_MORUS1280_SSE2 is not set +# CONFIG_CRYPTO_MORUS1280_AVX2 is not set +# CONFIG_CRYPTO_SEQIV is not set +CONFIG_CRYPTO_ECHAINIV=y + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CFB is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set +# CONFIG_CRYPTO_KEYWRAP is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_CMAC is not set +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32C_INTEL is not set +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_CRC32_PCLMUL is not set +# CONFIG_CRYPTO_CRCT10DIF is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_POLY1305 is not set +# CONFIG_CRYPTO_POLY1305_X86_64 is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA1_SSSE3 is not set +# CONFIG_CRYPTO_SHA256_SSSE3 is not set +# CONFIG_CRYPTO_SHA512_SSSE3 is not set +# CONFIG_CRYPTO_SHA1_MB is not set +# CONFIG_CRYPTO_SHA256_MB is not set +# CONFIG_CRYPTO_SHA512_MB is not set +CONFIG_CRYPTO_SHA256=y +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_SHA3 is not set +# CONFIG_CRYPTO_SM3 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_TI is not set +# CONFIG_CRYPTO_AES_X86_64 is not set +# CONFIG_CRYPTO_AES_NI_INTEL is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_BLOWFISH_X86_64 is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAMELLIA_X86_64 is not set +# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64 is not set +# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST5_AVX_X86_64 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_CAST6_AVX_X86_64 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_DES3_EDE_X86_64 is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_CHACHA20 is not set +# CONFIG_CRYPTO_CHACHA20_X86_64 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_SERPENT_SSE2_X86_64 is not set +# CONFIG_CRYPTO_SERPENT_AVX_X86_64 is not set +# CONFIG_CRYPTO_SERPENT_AVX2_X86_64 is not set +# CONFIG_CRYPTO_SM4 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_TWOFISH_X86_64 is not set +# CONFIG_CRYPTO_TWOFISH_X86_64_3WAY is not set +# CONFIG_CRYPTO_TWOFISH_AVX_X86_64 is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_842 is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set +# CONFIG_CRYPTO_ZSTD is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_DRBG_HMAC=y +# CONFIG_CRYPTO_DRBG_HASH is not set +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_JITTERENTROPY=y +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_USER_API_RNG is not set +# CONFIG_CRYPTO_USER_API_AEAD is not set +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_DEV_PADLOCK is not set +# CONFIG_CRYPTO_DEV_CCP is not set +# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set +# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set +# CONFIG_CRYPTO_DEV_QAT_C62X is not set +# CONFIG_CRYPTO_DEV_QAT_DH895xCCVF is not set +# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set +# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set +CONFIG_CRYPTO_DEV_VIRTIO=y + +# +# Certificates for signature checking +# + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_RATIONAL=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_NET_UTILS=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IOMAP=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y +CONFIG_ARCH_HAS_FAST_MULTIPLIER=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +CONFIG_CRC_ITU_T=y +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC64 is not set +# CONFIG_CRC4 is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +# CONFIG_RANDOM32_SELFTEST is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_DECOMPRESS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_DECOMPRESS_LZ4=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HAS_DMA=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_DMA_DIRECT_OPS=y +CONFIG_SWIOTLB=y +CONFIG_SGL_ALLOC=y +CONFIG_DQL=y +CONFIG_GLOB=y +# CONFIG_GLOB_SELFTEST is not set +CONFIG_NLATTR=y +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set +# CONFIG_IRQ_POLL is not set +CONFIG_SG_POOL=y +CONFIG_ARCH_HAS_SG_CHAIN=y +CONFIG_ARCH_HAS_PMEM_API=y +CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE=y +CONFIG_SBITMAP=y +# CONFIG_STRING_SELFTEST is not set + +# +# Kernel hacking +# + +# +# printk and dmesg options +# +# CONFIG_PRINTK_TIME is not set +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 +CONFIG_CONSOLE_LOGLEVEL_QUIET=4 +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 +# CONFIG_BOOT_PRINTK_DELAY is not set + +# +# Compile-time checks and compiler options +# +# CONFIG_DEBUG_INFO is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=1024 +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_PAGE_OWNER is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set +# CONFIG_STACK_VALIDATION is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_MAGIC_SYSRQ is not set +CONFIG_DEBUG_KERNEL=y + +# +# Memory Debugging +# +# CONFIG_PAGE_EXTENSION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_PAGE_POISONING is not set +# CONFIG_DEBUG_RODATA_TEST is not set +# CONFIG_DEBUG_OBJECTS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_VM is not set +CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y +# CONFIG_DEBUG_VIRTUAL is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +CONFIG_HAVE_DEBUG_STACKOVERFLOW=y +# CONFIG_DEBUG_STACKOVERFLOW is not set +CONFIG_HAVE_ARCH_KASAN=y +CONFIG_ARCH_HAS_KCOV=y +CONFIG_CC_HAS_SANCOV_TRACE_PC=y +# CONFIG_KCOV is not set +# CONFIG_DEBUG_SHIRQ is not set + +# +# Debug Lockups and Hangs +# +# CONFIG_SOFTLOCKUP_DETECTOR is not set +CONFIG_HARDLOCKUP_CHECK_TIMESTAMP=y +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_WQ_WATCHDOG is not set +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_SCHED_STACK_END_CHECK is not set +# CONFIG_DEBUG_TIMEKEEPING is not set + +# +# Lock Debugging (spinlocks, mutexes, etc...) +# +CONFIG_LOCK_DEBUGGING_SUPPORT=y +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_LOCK_TORTURE_TEST is not set +# CONFIG_WW_MUTEX_SELFTEST is not set +# CONFIG_STACKTRACE is not set +# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_PI_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set + +# +# RCU Debugging +# +# CONFIG_RCU_PERF_TEST is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_RCU_EQS_DEBUG is not set +# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +CONFIG_USER_STACKTRACE_SUPPORT=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_FENTRY=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_RUNTIME_TESTING_MENU is not set +# CONFIG_MEMTEST is not set +# CONFIG_BUG_ON_DATA_CORRUPTION is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y +# CONFIG_UBSAN is not set +CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +# CONFIG_X86_VERBOSE_BOOTUP is not set +# CONFIG_EARLY_PRINTK is not set +# CONFIG_X86_PTDUMP is not set +# CONFIG_DEBUG_WX is not set +# CONFIG_DOUBLEFAULT is not set +# CONFIG_DEBUG_TLBFLUSH is not set +CONFIG_HAVE_MMIOTRACE_SUPPORT=y +CONFIG_IO_DELAY_TYPE_0X80=0 +CONFIG_IO_DELAY_TYPE_0XED=1 +CONFIG_IO_DELAY_TYPE_UDELAY=2 +CONFIG_IO_DELAY_TYPE_NONE=3 +CONFIG_IO_DELAY_0X80=y +# CONFIG_IO_DELAY_0XED is not set +# CONFIG_IO_DELAY_UDELAY is not set +# CONFIG_IO_DELAY_NONE is not set +CONFIG_DEFAULT_IO_DELAY_TYPE=0 +# CONFIG_CPA_DEBUG is not set +CONFIG_OPTIMIZE_INLINING=y +# CONFIG_DEBUG_ENTRY is not set +# CONFIG_DEBUG_NMI_SELFTEST is not set +# CONFIG_X86_DEBUG_FPU is not set +# CONFIG_PUNIT_ATOM_DEBUG is not set +# CONFIG_UNWINDER_ORC is not set +# CONFIG_UNWINDER_FRAME_POINTER is not set +CONFIG_UNWINDER_GUESS=y diff --git a/vendor/github.com/systemboot/systemboot/.travis/tests.sh b/vendor/github.com/systemboot/systemboot/.travis/tests.sh index ce77be6..6862d13 100755 --- a/vendor/github.com/systemboot/systemboot/.travis/tests.sh +++ b/vendor/github.com/systemboot/systemboot/.travis/tests.sh @@ -3,7 +3,7 @@ # because things are never simple. # See https://github.com/codecov/example-go#caveat-multiple-files -set -e +set -ex echo "" > coverage.txt for d in $(go list ./... | grep -v vendor); do @@ -13,3 +13,7 @@ for d in $(go list ./... | grep -v vendor); do rm profile.out fi done + +# build systemboot using u-root +go get -u github.com/u-root/u-root +"${GOPATH}/bin/u-root" -build=bb core localboot netboot uinit diff --git a/vendor/github.com/systemboot/systemboot/README.md b/vendor/github.com/systemboot/systemboot/README.md index 67e4e23..07c0d56 100644 --- a/vendor/github.com/systemboot/systemboot/README.md +++ b/vendor/github.com/systemboot/systemboot/README.md @@ -1,12 +1,24 @@ # systemboot -SystemBoot is a distribution for LinuxBoot to create a system firmware + bootloader. It is based on u-root. The provided programs are: +[![Build Status](https://travis-ci.org/systemboot/systemboot.svg?branch=master)](https://travis-ci.org/systemboot/systemboot) +[![codecov](https://codecov.io/gh/systemboot/systemboot/branch/master/graph/badge.svg)](https://codecov.io/gh/systemboot/systemboot) +[![Go Report Card](https://goreportcard.com/badge/github.com/systemboot/systemboot)](https://goreportcard.com/report/github.com/systemboot/systemboot) + +> Note: systemboot [has been merged into u-root](https://github.com/u-root/u-root/pull/1255). +> This repository is now read-only and staying only for historical reasons, but you should build +> your bootloader entirely from u-root. +> How? The CLI tools have been moved under [u-root/cmds/boot](https://github.com/u-root/u-root/tree/master/cmds/boot/){fbnetboot,localboot,uinit}, +> the libraries under [u-root/pkg/]{bootconfig,booter,checker,crypto,recovery,rng,storage,vpd}. +> [u-root/tools/vpdbootmanager](https://github.com/u-root/u-root/tree/master/tools/vpdbootmanager) and +> [u-root/examples/fixmynetboot](https://github.com/u-root/u-root/tree/master/examples/fixmynetboot). + +SystemBoot is a distribution for LinuxBoot to create a system firmware + bootloader. It is based on [u-root](https://github.com/u-root/u-root). The provided programs are: * `netboot`: a network boot client that uses DHCP and HTTP to get a boot program based on Linux, and uses kexec to run it * `localboot`: a tool that finds bootable kernel configurations on the local disks and boots them * `uinit`: a wrapper around `netboot` and `localboot` that just mimicks a BIOS/UEFI BDS behaviour, by looping between network booting and local booting. The name `uinit` is necessary to be picked up as boot program by u-root. -This work is similar to the `pxeboot` and `boot` commands that are already part of u-root, but approach and implementation are slightly different. Thanks to Chris Koch and Jean-Marie Verdun for pioneering in this area. +This work is similar to the `pxeboot` and `boot` commands that are already part of u-root, but approach and implementation are slightly different. Thanks to Chris Koch and Jean-Marie Verdun for pioneering in this area. This project started as a personal experiment under github.com/insomniacslk/systemboot but it is now an effort of a broader community and graduated to a real project for system firmwares. @@ -44,11 +56,46 @@ In the future I will also support VPD, which will be used as a substitute for EF The `uinit` program just wraps `netboot` and `localboot` in a forever-loop logic, just like your BIOS/UEFI would do. At the moment it just loops between netboot and localboot in this order, but I plan to make this more flexible and configurable. +## How to build systemboot + +* Install a recent version of Go, we recommend 1.10 or later +* make sure that your PATH points appropriately to wherever Go stores the + go-get'ed executables +* Then build it with the `u-root` ramfs builder using the following commands: + +``` +go get -u github.com/u-root/u-root +go get -u github.com/systemboot/systemboot/{uinit,localboot,netboot} +u-root -build=bb core github.com/systemboot/systemboot/{uinit,localboot,netboot} +``` + +The initramfs will be located in `/tmp/initramfs_${platform}_${arch}.cpio`. + +More detailed information about the build process for a full LinuxBoot firmware image +using u-root/systemboot and coreboot can be found in the [LinuxBoot book](https://github.com/linuxboot/book) +chapter 11, [LinuxBoot using coreboot, u-root and systemboot](https://github.com/linuxboot/book/blob/master/11.coreboot.u-root.systemboot/README.md). + +## Example: LinuxBoot with coreboot + +One of the ways to create a LinuxBoot system firmware is by using +[coreboot](https://coreboot.org) do the basic silicon and DRAM initialization, +and then run Linux as payload, with u-root and systemboot as initramfs. See the +following diagram: + +![LinuxBoot and coreboot](resources/LinuxBoot.png) +(images from coreboot.org and wikipedia.org, diagram generated with draw.io) + +## Build and run as a fully open source bootloader in Qemu + +Systemboot is one of the parts of a bigger picture: running Linux as firmware. +We call this [LinuxBoot](https://linuxboot.org), and it can be achieved in various +ways. One of these is by combining [coreboot](https://coreboot.org), [Linux](https://kernel.org), +[u-root](https://u-root.tk) and `systemboot`. Check out the instructions on the +[LinuxBoot using coreboot, u-root and systemboot](https://github.com/linuxboot/book/tree/master/11.coreboot.u-root.systemboot) +chapter of the [LinuxBoot Book](https://github.com/linuxboot/book). + ## TODO -* DHCPv4 is under work -* VPD -* TPM support * verified and measured boot * a proper GRUB config parser * backwards compatibility with BIOS-style partitions diff --git a/vendor/github.com/systemboot/systemboot/cmds/fixmynetboot/main.go b/vendor/github.com/systemboot/systemboot/cmds/fixmynetboot/main.go new file mode 100644 index 0000000..66d5d3b --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/cmds/fixmynetboot/main.go @@ -0,0 +1,105 @@ +package main + +import ( + "flag" + "fmt" + "log" + "net" + + "github.com/systemboot/systemboot/pkg/checker" +) + +// fixmynetboot is a troubleshooting tool that can help you identify issues that +// won't let your system boot over the network. +// NOTE: this is a DEMO tool. It's here only to show you how to write your own +// checks and remediations. Don't use it in production. + +var emergencyShellBanner = ` +************************************************************************** +** Interface checks failed, see the output above to debug the issue. * +** Entering the emergency shell, where you can run "fixmynetboot" again, * +** or any other LinuxBoot command. * +************************************************************************** +` + +var ( + doEmergencyShell = flag.Bool("shell", false, "Run emergency shell if checks fail") +) + +func checkInterface(ifname string) error { + checklist := []checker.Check{ + checker.Check{ + Name: fmt.Sprintf("%s exists", ifname), + Run: checker.InterfaceExists(ifname), + Remediate: checker.InterfaceRemediate(ifname), + StopOnError: true, + }, + checker.Check{ + Name: fmt.Sprintf("%s link speed", ifname), + Run: checker.LinkSpeed(ifname, 100), + Remediate: nil, + StopOnError: false}, + checker.Check{ + Name: fmt.Sprintf("%s link autoneg", ifname), + Run: checker.LinkAutoneg(ifname, true), + Remediate: nil, + StopOnError: false, + }, + checker.Check{ + Name: fmt.Sprintf("%s has link-local", ifname), + Run: checker.InterfaceHasLinkLocalAddress(ifname), + Remediate: nil, + StopOnError: true, + }, + checker.Check{ + Name: fmt.Sprintf("%s has global addresses", ifname), + Run: checker.InterfaceHasGlobalAddresses("eth0"), + Remediate: nil, + StopOnError: true, + }, + } + + return checker.Run(checklist) +} + +func getNonLoopbackInterfaces() ([]string, error) { + var interfaces []string + allInterfaces, err := net.Interfaces() + if err != nil { + return nil, err + } + for _, iface := range allInterfaces { + if iface.Flags&net.FlagLoopback == 0 { + interfaces = append(interfaces, iface.Name) + } + } + return interfaces, nil +} + +func main() { + flag.Parse() + var ( + interfaces []string + err error + ) + ifname := flag.Arg(0) + if ifname == "" { + interfaces, err = getNonLoopbackInterfaces() + if err != nil { + log.Fatal(err) + } + } else { + interfaces = []string{ifname} + } + + for _, ifname := range interfaces { + if err := checkInterface(ifname); err != nil { + if !*doEmergencyShell { + log.Fatal(err) + } + if err := checker.EmergencyShell(emergencyShellBanner)(); err != nil { + log.Fatal(err) + } + } + } +} diff --git a/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/add.go b/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/add.go new file mode 100644 index 0000000..ec4c8a8 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/add.go @@ -0,0 +1,131 @@ +package main + +import ( + "encoding/json" + "errors" + "flag" + "fmt" + "net" + "os" + + "github.com/systemboot/systemboot/pkg/booter" + "github.com/systemboot/systemboot/pkg/vpd" +) + +var dryRun = false + +func add(entrytype string, args []string) error { + var entry booter.Booter + var err error + switch entrytype { + case "netboot": + if len(args) < 2 { + return fmt.Errorf("You need to pass method and MAC address") + } + entry, err = parseNetbootFlags(args[0], args[1], args[2:]) + if err != nil { + return err + } + case "localboot": + if len(args) < 1 { + return fmt.Errorf("You need to provide method") + } + entry, err = parseLocalbootFlags(args[0], args[1:]) + if err != nil { + return err + } + default: + return fmt.Errorf("Unknown entry type") + } + if dryRun { + b, err := json.Marshal(entry) + if err != nil { + return err + } + fmt.Fprintln(os.Stderr, "Using -dryrun, will not write any variable. Content of boot entry:") + fmt.Println(string(b)) + return nil + } + return addBootEntry(entry) +} + +func parseLocalbootFlags(method string, args []string) (*booter.LocalBooter, error) { + cfg := &booter.LocalBooter{ + Type: "localboot", + Method: method, + } + flg := flag.NewFlagSet("localboot", flag.ExitOnError) + flg.StringVar(&cfg.KernelArgs, "kernel-args", "", "additional kernel args") + flg.StringVar(&cfg.Initramfs, "ramfs", "", "path of ramfs to be used for kexec'ing into the target kernel.") + flg.StringVar(&vpd.VpdDir, "vpd-dir", vpd.VpdDir, "VPD dir to use") + flg.BoolVar(&dryRun, "dryrun", false, "only print values that would be set") + + switch method { + case "grub": + flg.Parse(args) + case "path": + if len(args) < 2 { + return nil, fmt.Errorf("You need to pass DeviceGUID and Kernel path") + } + cfg.DeviceGUID = args[0] + cfg.Kernel = args[1] + flg.Parse(args[2:]) + default: + return nil, fmt.Errorf("Method needs to be grub or path") + } + return cfg, nil +} + +func parseNetbootFlags(method, mac string, args []string) (*booter.NetBooter, error) { + if method != "dhcpv4" && method != "dhcpv6" { + return nil, fmt.Errorf("Method needs to be either dhcpv4 or dhcpv6") + } + + _, err := net.ParseMAC(mac) + if err != nil { + return nil, err + } + + cfg := &booter.NetBooter{ + Type: "netboot", + Method: method, + MAC: mac, + } + + flg := flag.NewFlagSet("netboot", flag.ExitOnError) + overrideURL := flg.String("override-url", "", "an optional URL used to override the boot file URL used") + retries := flg.Int("retries", -1, "the number of times a DHCP request should be retried if failed.") + flg.BoolVar(&dryRun, "dryrun", false, "only print values that would be set") + flg.StringVar(&vpd.VpdDir, "vpd-dir", vpd.VpdDir, "VPD dir to use") + flg.Parse(args) + + if *overrideURL != "" { + cfg.OverrideURL = overrideURL + } + + if *retries != -1 { + cfg.Retries = retries + } + + return cfg, nil +} + +func addBootEntry(cfg booter.Booter) error { + data, err := json.Marshal(cfg) + if err != nil { + return err + } + for i := 1; i < vpd.MaxBootEntry; i++ { + key := fmt.Sprintf("Boot%04d", i) + if _, err := vpd.Get(key, false); err != nil { + if os.IsNotExist(err) { + if err := vpd.Set(key, data, false); err != nil { + return err + } + return nil + } + return err + } + } + return errors.New("Maximum number of boot entries already set") +} diff --git a/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/add_test.go b/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/add_test.go new file mode 100644 index 0000000..40deaf5 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/add_test.go @@ -0,0 +1,170 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "path" + "testing" + + "github.com/systemboot/systemboot/pkg/booter" + + "github.com/systemboot/systemboot/pkg/vpd" + + "github.com/stretchr/testify/require" +) + +func TestParseNetboot(t *testing.T) { + b, err := parseNetbootFlags("dhcpv4", "aa:bb:cc:dd:ee:ff", []string{}) + require.NoError(t, err) + require.Equal(t, "netboot", b.Type) + require.Equal(t, "dhcpv4", b.Method) + require.Equal(t, "aa:bb:cc:dd:ee:ff", b.MAC) + require.Nil(t, b.OverrideURL) + require.Nil(t, b.Retries) +} + +func TestParseNetbootWithFlags(t *testing.T) { + b, err := parseNetbootFlags("dhcpv4", "aa:bb:cc:dd:ee:ff", []string{ + "-override-url", + "http://url", + "-retries", + "1", + "-vpd-dir", + "test", + }) + require.NoError(t, err) + require.Equal(t, "http://url", *b.OverrideURL) + require.Equal(t, 1, *b.Retries) + require.Equal(t, "test", vpd.VpdDir) +} + +func TestParseLocalboot(t *testing.T) { + b, err := parseLocalbootFlags("grub", []string{}) + require.NoError(t, err) + require.Equal(t, "grub", b.Method) + + b, err = parseLocalbootFlags("path", []string{ + "device", + "path", + }) + require.NoError(t, err) + require.Equal(t, "path", b.Method) + require.Equal(t, "device", b.DeviceGUID) + require.Equal(t, "path", b.Kernel) +} + +func TestParseLocalbootWithFlags(t *testing.T) { + b, err := parseLocalbootFlags("grub", []string{ + "-kernel-args", + "kernel-argument-test", + "-ramfs", + "ramfs-test", + "-vpd-dir", + "test", + }) + require.NoError(t, err) + require.Equal(t, "grub", b.Method) + require.Equal(t, "kernel-argument-test", b.KernelArgs) + require.Equal(t, "ramfs-test", b.Initramfs) + require.Equal(t, "test", vpd.VpdDir) + + b, err = parseLocalbootFlags("path", []string{ + "device", + "path", + "-kernel-args", + "kernel-argument-test", + "-ramfs", + "ramfs-test", + "-vpd-dir", + "test", + }) + require.NoError(t, err) + require.Equal(t, "path", b.Method) + require.Equal(t, "device", b.DeviceGUID) + require.Equal(t, "path", b.Kernel) + require.Equal(t, "kernel-argument-test", b.KernelArgs) + require.Equal(t, "ramfs-test", b.Initramfs) + require.Equal(t, "test", vpd.VpdDir) +} + +func TestFailGracefullyMissingArg(t *testing.T) { + err := add("localboot", []string{}) + require.Equal(t, "You need to provide method", err.Error()) + + err = add("localboot", []string{"path"}) + require.Equal(t, "You need to pass DeviceGUID and Kernel path", err.Error()) + + err = add("localboot", []string{"path", "device"}) + require.Equal(t, "You need to pass DeviceGUID and Kernel path", err.Error()) + + err = add("netboot", []string{}) + require.Equal(t, "You need to pass method and MAC address", err.Error()) + + err = add("netboot", []string{"dhcpv6"}) + require.Equal(t, "You need to pass method and MAC address", err.Error()) +} + +func TestFailGracefullyBadMACAddress(t *testing.T) { + err := add("netboot", []string{"dhcpv6", "test"}) + require.Equal(t, "address test: invalid MAC address", err.Error()) +} + +func TestFailGracefullyBadNetworkType(t *testing.T) { + err := add("netboot", []string{"not-valid", "test"}) + require.Equal(t, "Method needs to be either dhcpv4 or dhcpv6", err.Error()) +} + +func TestFailGracefullyBadLocalbootType(t *testing.T) { + err := add("localboot", []string{"not-valid"}) + require.Equal(t, "Method needs to be grub or path", err.Error()) +} + +func TestFailGracefullyUnknownEntryType(t *testing.T) { + err := add("test", []string{}) + require.Equal(t, "Unknown entry type", err.Error()) +} + +func TestAddBootEntry(t *testing.T) { + dir, err := ioutil.TempDir("", "vpdbootmanager") + if err != nil { + log.Fatal(err) + } + os.MkdirAll(path.Join(dir, "rw"), 0700) + defer os.RemoveAll(dir) + vpd.VpdDir = dir + err = addBootEntry(&booter.LocalBooter{ + Method: "grub", + }) + require.NoError(t, err) + file, err := ioutil.ReadFile(path.Join(dir, "rw", "Boot0001")) + require.NoError(t, err) + var out booter.LocalBooter + err = json.Unmarshal([]byte(file), &out) + require.NoError(t, err) + require.Equal(t, "grub", out.Method) +} + +func TestAddBootEntryMultiple(t *testing.T) { + dir, err := ioutil.TempDir("", "vpdbootmanager") + if err != nil { + log.Fatal(err) + } + os.MkdirAll(path.Join(dir, "rw"), 0700) + defer os.RemoveAll(dir) + vpd.VpdDir = dir + for i := 1; i < 5; i++ { + err = addBootEntry(&booter.LocalBooter{ + Method: "grub", + }) + require.NoError(t, err) + file, err := ioutil.ReadFile(path.Join(dir, "rw", fmt.Sprintf("Boot%04d", i))) + require.NoError(t, err) + var out booter.LocalBooter + err = json.Unmarshal([]byte(file), &out) + require.NoError(t, err) + require.Equal(t, "grub", out.Method) + } +} diff --git a/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/main.go b/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/main.go new file mode 100644 index 0000000..0edf0fa --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/main.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "os" +) + +const usage = `Usage: +%s add [netboot [dhcpv6|dhcpv4] [MAC] | localboot [grub|path [Device GUID] [Kernel Path]]] + +Ex. +add localboot grub +add netboot dhcpv6 AA:BB:CC:DD:EE:FF + +Flags for netboot: + +-override-url - an optional URL used to override the boot file URL used +-retries - the number of times a DHCP request should be retried if failed + +Flags for localboot: + +-kernel-args - additional kernel args +-ramfs - path of ramfs to be used for kexec'ing into the target kernel + +Global flags: + +-vpd-dir - VPD dir to use + +` + +func main() { + if err := cli(os.Args[1:]); err != nil { + fmt.Printf(usage, os.Args[0]) + fmt.Printf("Error: %s\n\n", err) + os.Exit(1) + } +} + +func cli(args []string) error { + if len(args) < 1 { + return fmt.Errorf("You need to provide action") + } + switch args[0] { + case "add": + return add(args[1], args[2:]) + } + return fmt.Errorf("Unrecognized action") +} diff --git a/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/main_test.go b/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/main_test.go new file mode 100644 index 0000000..40d0e5f --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/cmds/vpdbootmanager/main_test.go @@ -0,0 +1,76 @@ +package main + +import ( + "encoding/json" + "io/ioutil" + "log" + "os" + "path" + "testing" + + "github.com/stretchr/testify/require" + "github.com/systemboot/systemboot/pkg/booter" +) + +func TestInvalidCommand(t *testing.T) { + err := cli([]string{"unknown"}) + require.Equal(t, "Unrecognized action", err.Error()) +} + +func TestNoEntryType(t *testing.T) { + err := cli([]string{"add", "localboot"}) + require.Equal(t, "You need to provide method", err.Error()) +} + +func TestNoAction(t *testing.T) { + err := cli([]string{}) + require.Equal(t, "You need to provide action", err.Error()) +} + +func TestAddNetbootEntryFull(t *testing.T) { + dir, err := ioutil.TempDir("", "vpdbootmanager") + if err != nil { + log.Fatal(err) + } + os.MkdirAll(path.Join(dir, "rw"), 0700) + defer os.RemoveAll(dir) + err = cli([]string{ + "add", + "netboot", + "dhcpv6", + "aa:bb:cc:dd:ee:ff", + "-vpd-dir", + dir, + }) + require.NoError(t, err) + file, err := ioutil.ReadFile(path.Join(dir, "rw", "Boot0001")) + require.NoError(t, err) + var out booter.NetBooter + err = json.Unmarshal([]byte(file), &out) + require.NoError(t, err) + require.Equal(t, "dhcpv6", out.Method) + require.Equal(t, "aa:bb:cc:dd:ee:ff", out.MAC) +} + +func TestAddLocalbootEntryFull(t *testing.T) { + dir, err := ioutil.TempDir("", "vpdbootmanager") + if err != nil { + log.Fatal(err) + } + os.MkdirAll(path.Join(dir, "rw"), 0700) + defer os.RemoveAll(dir) + err = cli([]string{ + "add", + "localboot", + "grub", + "-vpd-dir", + dir, + }) + require.NoError(t, err) + file, err := ioutil.ReadFile(path.Join(dir, "rw", "Boot0001")) + require.NoError(t, err) + var out booter.NetBooter + err = json.Unmarshal([]byte(file), &out) + require.NoError(t, err) + require.Equal(t, "grub", out.Method) +} diff --git a/vendor/github.com/systemboot/systemboot/localboot/bootconfig.go b/vendor/github.com/systemboot/systemboot/localboot/bootconfig.go deleted file mode 100644 index 6d2389d..0000000 --- a/vendor/github.com/systemboot/systemboot/localboot/bootconfig.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "os" - - "github.com/u-root/u-root/pkg/kexec" -) - -// BootConfig holds information to boot a kernel using kexec -type BootConfig struct { - Kernel *os.File - // KernelName is used only for reference but has no effect on booting - KernelName string - Initrd *os.File - // InitrdName is used only for reference but has no effect on booting - InitrdName string - Cmdline string -} - -// IsValid returns true if the BootConfig has a valid kernel and initrd entry -func (bc BootConfig) IsValid() bool { - return bc.Kernel != nil && bc.Initrd != nil -} - -// Boot tries to boot the kernel pointed by the BootConfig option, or returns an -// error if it cannot be booted. The kernel is loaded using kexec -func (bc BootConfig) Boot() error { - if err := kexec.FileLoad(bc.Kernel, bc.Initrd, bc.Cmdline); err != nil { - return err - } - kexec.Reboot() - // this should be never reached - return nil -} - -// Close will close all the open file descriptor used for kernel and initrd -func (bc *BootConfig) Close() { - if bc.Kernel != nil { - bc.Kernel.Close() - bc.Kernel = nil - bc.KernelName = "" - } - if bc.Initrd != nil { - bc.Initrd.Close() - bc.Initrd = nil - bc.InitrdName = "" - } -} diff --git a/vendor/github.com/systemboot/systemboot/localboot/grub.go b/vendor/github.com/systemboot/systemboot/localboot/grub.go index 23f5b83..b6e35c3 100644 --- a/vendor/github.com/systemboot/systemboot/localboot/grub.go +++ b/vendor/github.com/systemboot/systemboot/localboot/grub.go @@ -1,48 +1,72 @@ package main import ( + "encoding/hex" "io/ioutil" "log" "os" "path" + "path/filepath" "strings" + "unicode" + + "golang.org/x/text/transform" + "golang.org/x/text/unicode/norm" + + "github.com/systemboot/systemboot/pkg/bootconfig" + "github.com/systemboot/systemboot/pkg/storage" ) -// List of paths where to look for grub config files. Grub2Paths will look for -// files with grub2-compatible syntax, GrubLegacyPaths similarly will treat -// these as grub-legacy config files. +// List of directories where to recursively look for grub config files. The root dorectory +// of each mountpoint, these folders inside the mountpoint and all subfolders +// of these folders are searched var ( - Grub2Paths = []string{ - // grub2 - "boot/grub2/grub.cfg", - "boot/grub2.cfg", - "grub2/grub.cfg", - "grub2.cfg", - } - GrubLegacyPaths = []string{ - // grub legacy - "boot/grub/grub.cfg", - "boot/grub.cfg", - "grub/grub.cfg", - "grub.cfg", + GrubSearchDirectories = []string{ + "boot", + "EFI", + "efi", + "grub", + "grub2", } ) +// Limits rekursive search of grub files. It is the maximum directory depth +// that is searched through. Since on efi partitions grub files reside usually +// at /boot/efi/EFI/distro/ , 4 might be a good choice. +const searchDepth = 4 + +type grubVersion int + +var ( + grubV1 grubVersion = 1 + grubV2 grubVersion = 2 +) + +func isGrubSearchDir(dirname string) bool { + for _, dir := range GrubSearchDirectories { + if dirname == dir { + return true + } + } + return false +} + // ParseGrubCfg parses the content of a grub.cfg and returns a list of // BootConfig structures, one for each menuentry, in the same order as they // appear in grub.cfg. All opened kernel and initrd files are relative to // basedir. -func ParseGrubCfg(grubcfg string, basedir string, grubVersion int) []BootConfig { +func ParseGrubCfg(ver grubVersion, devices []storage.BlockDev, grubcfg string, basedir string) []bootconfig.BootConfig { // This parser sucks. It's not even a parser, it just looks for lines // starting with menuentry, linux or initrd. // TODO use a parser, e.g. https://github.com/alecthomas/participle - if grubVersion != 1 && grubVersion != 2 { - log.Printf("Warning: invalid GRUB version: %d", grubVersion) + if ver != grubV1 && ver != grubV2 { + log.Printf("Warning: invalid GRUB version: %d", ver) return nil } - bootconfigs := make([]BootConfig, 0) + kernelBasedir := basedir + bootconfigs := make([]bootconfig.BootConfig, 0) inMenuEntry := false - var cfg *BootConfig + var cfg *bootconfig.BootConfig for _, line := range strings.Split(grubcfg, "\n") { // remove all leading spaces as they are not relevant for the config // line @@ -60,10 +84,46 @@ func ParseGrubCfg(grubcfg string, basedir string, grubVersion int) []BootConfig // both kernel and initramfs bootconfigs = append(bootconfigs, *cfg) } + // reset kernelBaseDir + kernelBasedir = basedir } inMenuEntry = true - cfg = new(BootConfig) + cfg = new(bootconfig.BootConfig) + name := "" + if len(sline) > 1 { + name = strings.Join(sline[1:], " ") + name = unquote(ver, name) + name = strings.Split(name, "--")[0] + } + cfg.Name = name } else if inMenuEntry { + // check if location of kernel is at an other partition + // see https://www.gnu.org/software/grub/manual/grub/html_node/search.html + if sline[0] == "search" { + for _, str1 := range sline { + if str1 == "--set=root" { + log.Printf("Kernel seems to be on an other partitioin then the grub.cfg file") + for _, str2 := range sline { + if isValidFsUUID(str2) { + kernelFsUUID := str2 + log.Printf("fs-uuid: %s", kernelFsUUID) + partitions := storage.PartitionsByFsUUID(devices, kernelFsUUID) + if len(partitions) == 0 { + log.Printf("WARNING: No partition found with filesystem UUID:'%s' to load kernel from!", kernelFsUUID) // TODO throw error ? + continue + } + if len(partitions) > 1 { + log.Printf("WARNING: more than one partition found with the given filesystem UUID. Using the first one") + } + dev := partitions[0] + kernelBasedir = path.Dir(kernelBasedir) + kernelBasedir = path.Join(kernelBasedir, dev.Name) + log.Printf("Kernel is on: %s", dev.Name) + } + } + } + } + } // otherwise look for kernel and initramfs configuration if len(sline) < 2 { // surely not a valid linux or initrd directive, skip it @@ -72,32 +132,31 @@ func ParseGrubCfg(grubcfg string, basedir string, grubVersion int) []BootConfig if sline[0] == "linux" || sline[0] == "linux16" || sline[0] == "linuxefi" { kernel := sline[1] cmdline := strings.Join(sline[2:], " ") - if grubVersion == 2 { - // if grub2, unquote the string, as directives could be quoted - // https://www.gnu.org/software/grub/manual/grub/grub.html#Quoting - // TODO unquote everything, not just \$ - cmdline = strings.Replace(cmdline, `\$`, "$", -1) - } - fullpath := path.Join(basedir, kernel) - fd, err := os.Open(fullpath) - if err != nil { - debug("error opening kernel file %s: %v", fullpath, err) - } - cfg.Kernel = fd - cfg.KernelName = kernel - cfg.Cmdline = cmdline + cmdline = unquote(ver, cmdline) + cfg.Kernel = path.Join(kernelBasedir, kernel) + cfg.KernelArgs = cmdline } else if sline[0] == "initrd" || sline[0] == "initrd16" || sline[0] == "initrdefi" { initrd := sline[1] - fullpath := path.Join(basedir, initrd) - fd, err := os.Open(fullpath) - if err != nil { - debug("error opening initrd file %s: %v", fullpath, err) + cfg.Initramfs = path.Join(kernelBasedir, initrd) + } else if sline[0] == "multiboot" || sline[0] == "multiboot2" { + multiboot := sline[1] + cmdline := strings.Join(sline[2:], " ") + cmdline = unquote(ver, cmdline) + cfg.Multiboot = path.Join(kernelBasedir, multiboot) + cfg.MultibootArgs = cmdline + } else if sline[0] == "module" || sline[0] == "module2" { + module := sline[1] + cmdline := strings.Join(sline[2:], " ") + cmdline = unquote(ver, cmdline) + module = path.Join(kernelBasedir, module) + if cmdline != "" { + module = module + " " + cmdline } - cfg.Initrd = fd - cfg.InitrdName = initrd + cfg.Modules = append(cfg.Modules, module) } } } + // append last kernel config if it wasn't already if inMenuEntry && cfg.IsValid() { bootconfigs = append(bootconfigs, *cfg) @@ -105,33 +164,81 @@ func ParseGrubCfg(grubcfg string, basedir string, grubVersion int) []BootConfig return bootconfigs } +func isValidFsUUID(uuid string) bool { + for _, h := range strings.Split(uuid, "-") { + if _, err := hex.DecodeString(h); err != nil { + return false + } + } + return true +} + +func unquote(ver grubVersion, text string) string { + if ver == grubV2 { + // if grub2, unquote the string, as directives could be quoted + // https://www.gnu.org/software/grub/manual/grub/grub.html#Quoting + // TODO unquote everything, not just \$ + return strings.Replace(text, `\$`, "$", -1) + } + // otherwise return the unmodified string + return text +} + +func isMn(r rune) bool { + return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks +} + // ScanGrubConfigs looks for grub2 and grub legacy config files in the known // locations and returns a list of boot configurations. -func ScanGrubConfigs(basedir string) []BootConfig { - bootconfigs := make([]BootConfig, 0) - // Scan Grub 2 configurations - for _, grubpath := range Grub2Paths { - path := path.Join(basedir, grubpath) - log.Printf("Trying to read %s", path) - grubcfg, err := ioutil.ReadFile(path) +func ScanGrubConfigs(devices []storage.BlockDev, basedir string) []bootconfig.BootConfig { + bootconfigs := make([]bootconfig.BootConfig, 0) + err := filepath.Walk(basedir, func(currentPath string, info os.FileInfo, err error) error { if err != nil { - log.Printf("cannot open %s: %v", path, err) - continue + return err } - cfgs := ParseGrubCfg(string(grubcfg), basedir, 2) - bootconfigs = append(bootconfigs, cfgs...) - } - // Scan Grub Legacy configurations - for _, grubpath := range GrubLegacyPaths { - path := path.Join(basedir, grubpath) - log.Printf("Trying to read %s", path) - grubcfg, err := ioutil.ReadFile(path) + t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC) + currentPath, _, _ = transform.String(t, currentPath) + if info.IsDir() { + if path.Dir(currentPath) == basedir && !isGrubSearchDir(path.Base(currentPath)) { + debug("Skip %s: not significant", currentPath) + // skip irrelevant toplevel directories + return filepath.SkipDir + } + p, err := filepath.Rel(basedir, currentPath) + if err != nil { + return err + } + depth := len(strings.Split(p, string(os.PathSeparator))) + if depth > searchDepth { + debug("Skip %s, depth limit", currentPath) + // skip + return filepath.SkipDir + } + debug("Step into %s", currentPath) + // continue + return nil + } + cfgname := info.Name() + var ver grubVersion + switch cfgname { + case "grub.cfg": + ver = grubV1 + case "grub2.cfg": + ver = grubV2 + default: + return nil + } + log.Printf("Parsing %s", currentPath) + data, err := ioutil.ReadFile(currentPath) if err != nil { - log.Printf("cannot open %s: %v", path, err) - continue + return err } - cfgs := ParseGrubCfg(string(grubcfg), basedir, 1) + cfgs := ParseGrubCfg(ver, devices, string(data), basedir) bootconfigs = append(bootconfigs, cfgs...) + return nil + }) + if err != nil { + log.Printf("filepath.Walk error: %v", err) } return bootconfigs } diff --git a/vendor/github.com/systemboot/systemboot/localboot/main.go b/vendor/github.com/systemboot/systemboot/localboot/main.go index 61b5f5e..0167ca9 100644 --- a/vendor/github.com/systemboot/systemboot/localboot/main.go +++ b/vendor/github.com/systemboot/systemboot/localboot/main.go @@ -2,55 +2,78 @@ package main import ( "flag" + "fmt" "log" + "os" "path" "syscall" + "github.com/systemboot/systemboot/pkg/bootconfig" "github.com/systemboot/systemboot/pkg/storage" ) // TODO backward compatibility for BIOS mode with partition type 0xee -// TODO read and write non-volatile variables on the flash ROM via VPD -// https://chromium.googlesource.com/chromiumos/platform/vpd/ -// via sysfs: -// https://github.com/torvalds/linux/blob/master/drivers/firmware/google/vpd.c // TODO use a proper parser for grub config (see grub.go) var ( - baseMountPoint = flag.String("m", "/mnt", "Base mount point where to mount partiions") - doDebug = flag.Bool("d", false, "Print debug output") + flagBaseMountPoint = flag.String("m", "/mnt", "Base mount point where to mount partitions") + flagDryRun = flag.Bool("dryrun", false, "Do not actually kexec into the boot config") + flagDebug = flag.Bool("d", false, "Print debug output") + flagConfigIdx = flag.Int("config", -1, "Specify the index of the configuration to boot. The order is determined by the menu entries in the Grub config") + flagGrubMode = flag.Bool("grub", false, "Use GRUB mode, i.e. look for valid Grub/Grub2 configuration in default locations to boot a kernel. GRUB mode ignores -kernel/-initramfs/-cmdline") + flagKernelPath = flag.String("kernel", "", "Specify the path of the kernel to execute. If using -grub, this argument is ignored") + flagInitramfsPath = flag.String("initramfs", "", "Specify the path of the initramfs to load. If using -grub, this argument is ignored") + flagKernelCmdline = flag.String("cmdline", "", "Specify the kernel command line. If using -grub, this argument is ignored") + flagDeviceGUID = flag.String("guid", "", "GUID of the device where the kernel (and optionally initramfs) are located. Ignored if -grub is set or if -kernel is not specified") ) var debug = func(string, ...interface{}) {} -func main() { - flag.Parse() - - if *doDebug { - debug = log.Printf +// mountByGUID looks for a partition with the given GUID, and tries to mount it +// in a subdirectory under the specified mount point. The subdirectory has the +// same name of the device (e.g. /your/base/mountpoint/sda1). +// The specified filesystems will be used in the mount attempts. +// If more than one partition is found with the given GUID, the first that is +// found is used. +// This function returns a storage.Mountpoint object, or an error if any. +func mountByGUID(devices []storage.BlockDev, filesystems []string, guid, baseMountpoint string) (*storage.Mountpoint, error) { + log.Printf("Looking for partition with GUID %s", guid) + partitions, err := storage.PartitionsByGUID(devices, guid) + if err != nil || len(partitions) == 0 { + return nil, fmt.Errorf("Error looking up for partition with GUID %s", guid) } - - // Get all the available block devices - devices, err := storage.GetBlockStats() - if err != nil { - log.Fatal(err) + log.Printf("Partitions with GUID %s: %+v", guid, partitions) + if len(partitions) > 1 { + log.Printf("Warning: more than one partition found with the given GUID. Using the first one") } - // print partition info - for _, dev := range devices { - log.Printf("Device: %+v", dev) - table, err := storage.GetGPTTable(dev) - if err != nil { - continue - } - log.Printf(" Table: %+v", table) - for _, part := range table.Partitions { - log.Printf(" Partition: %+v", part) - if !part.IsEmpty() { - log.Printf(" UUID: %s", part.Type.String()) - } - } + dev := partitions[0] + mountpath := path.Join(baseMountpoint, dev.Name) + devname := path.Join("/dev", dev.Name) + mountpoint, err := storage.Mount(devname, mountpath, filesystems) + if err != nil { + return nil, fmt.Errorf("mountByGUID: cannot mount %s (GUID %s) on %s: %v", devname, guid, mountpath, err) } + return mountpoint, nil +} +// BootGrubMode tries to boot a kernel in GRUB mode. GRUB mode means: +// * look for the partition with the specified GUID, and mount it +// * if no GUID is specified, mount all of the specified devices +// * try to mount the device(s) using any of the kernel-supported filesystems +// * look for a GRUB configuration in various well-known locations +// * build a list of valid boot configurations from the found GRUB configuration files +// * try to boot every valid boot configuration until one succeeds +// +// The first parameter, `devices` is a list of storage.BlockDev . The function +// will look for bootable configurations on these devices +// The second parameter, `baseMountPoint`, is the directory where the mount +// points for each device will be created. +// The third parameter, `guid`, is the partition GUID to look for. If it is an +// empty string, will search boot configurations on all of the specified devices +// instead. +// The fourth parameter, `dryrun`, will not boot the found configurations if set +// to true. +func BootGrubMode(devices []storage.BlockDev, baseMountpoint string, guid string, dryrun bool, configIdx int) error { // get a list of supported file systems for real devices (i.e. skip nodev) debug("Getting list of supported filesystems") filesystems, err := storage.GetSupportedFilesystems() @@ -59,56 +82,174 @@ func main() { } debug("Supported file systems: %v", filesystems) - // detect EFI system partitions - // TODO currently, this is not necessary, but will be once we have VPD. - debug("Searching for EFI system partitions") - esps, err := storage.FilterEFISystemPartitions(devices) - if err != nil { - log.Fatal(err) - } - log.Printf("Found %d system partitions: %+v", len(esps), esps) - - // try mounting all the available devices, with all the supported file - // systems - debug("trying to mount all the available block devices with all the supported file system types") - mounted := make([]storage.Mountpoint, 0) - for _, dev := range devices { - devname := path.Join("/dev", dev.Name) - mountpath := path.Join(*baseMountPoint, dev.Name) - if mountpoint, err := storage.Mount(devname, mountpath, filesystems); err != nil { - debug("Failed to mount %s on %s: %v", devname, mountpath, err) - } else { - mounted = append(mounted, *mountpoint) + var mounted []storage.Mountpoint + if guid == "" { + // try mounting all the available devices, with all the supported file + // systems + debug("trying to mount all the available block devices with all the supported file system types") + mounted = make([]storage.Mountpoint, 0) + for _, dev := range devices { + devname := path.Join("/dev", dev.Name) + mountpath := path.Join(baseMountpoint, dev.Name) + if mountpoint, err := storage.Mount(devname, mountpath, filesystems); err != nil { + debug("Failed to mount %s on %s: %v", devname, mountpath, err) + } else { + mounted = append(mounted, *mountpoint) + } } + log.Printf("mounted: %+v", mounted) + defer func() { + // clean up + for _, mountpoint := range mounted { + syscall.Unmount(mountpoint.Path, syscall.MNT_DETACH) + } + }() + } else { + mount, err := mountByGUID(devices, filesystems, guid, baseMountpoint) + if err != nil { + return err + } + mounted = []storage.Mountpoint{*mount} } - debug("mounted: %+v", mounted) // search for a valid grub config and extracts the boot configuration - bootconfigs := make([]BootConfig, 0) + bootconfigs := make([]bootconfig.BootConfig, 0) for _, mountpoint := range mounted { - bootconfigs = append(bootconfigs, ScanGrubConfigs(mountpoint.Path)...) + bootconfigs = append(bootconfigs, ScanGrubConfigs(devices, mountpoint.Path)...) + } + if len(bootconfigs) == 0 { + return fmt.Errorf("No boot configuration found") } log.Printf("Found %d boot configs", len(bootconfigs)) for _, cfg := range bootconfigs { debug("%+v", cfg) } + for n, cfg := range bootconfigs { + log.Printf(" %d: %s\n", n, cfg.Name) + } + if configIdx > -1 { + for n, cfg := range bootconfigs { + if configIdx == n { + if dryrun { + debug("Dry-run mode: will not boot the found configuration") + debug("Boot configuration: %+v", cfg) + return nil + } + if err := cfg.Boot(); err != nil { + log.Printf("Failed to boot kernel %s: %v", cfg.Kernel, err) + } + } + } + log.Printf("Invalid arg -config %d: there are only %d bootconfigs available\n", configIdx, len(bootconfigs)) + return nil + } + if dryrun { + cfg := bootconfigs[0] + debug("Dry-run mode: will not boot the found configuration") + debug("Boot configuration: %+v", cfg) + return nil + } // try to kexec into every boot config kernel until one succeeds for _, cfg := range bootconfigs { - log.Printf("trying to boot %s", cfg.KernelName) + debug("Trying boot configuration %+v", cfg) if err := cfg.Boot(); err != nil { - log.Printf("Failed to boot kernel %s: %v", cfg.KernelName, err) - cfg.Close() + log.Printf("Failed to boot kernel %s: %v", cfg.Kernel, err) } } + // if we reach this point, no boot configuration succeeded log.Print("No boot configuration succeeded") - // if we are here, booting failed, so let's clean things up by closing all - // open file descriptors and unmounting the devices that we mounted - for _, cfg := range bootconfigs { - cfg.Close() + return nil +} + +// BootPathMode tries to boot a kernel in PATH mode. This means: +// * look for a partition with the given GUID and mount it +// * look for the kernel and initramfs in the provided locations +// * boot the kernel with the provided command line +// +// The first parameter, `devices` is a list of storage.BlockDev . The function +// will look for bootable configurations on these devices +// The second parameter, `baseMountPoint`, is the directory where the mount +// points for each device will be created. +// The third parameter, `guid`, is the partition GUID to look for. +// The fourth parameter, `dryrun`, will not boot the found configurations if set +// to true. +func BootPathMode(devices []storage.BlockDev, baseMountpoint string, guid string, dryrun bool) error { + debug("Getting list of supported filesystems") + filesystems, err := storage.GetSupportedFilesystems() + if err != nil { + log.Fatal(err) } - for _, mountpoint := range mounted { - syscall.Unmount(mountpoint.Path, syscall.MNT_DETACH) + debug("Supported file systems: %v", filesystems) + + mount, err := mountByGUID(devices, filesystems, guid, baseMountpoint) + if err != nil { + return err + } + + fullKernelPath := path.Join(mount.Path, *flagKernelPath) + fullInitramfsPath := path.Join(mount.Path, *flagInitramfsPath) + cfg := bootconfig.BootConfig{ + Kernel: fullKernelPath, + Initramfs: fullInitramfsPath, + KernelArgs: *flagKernelCmdline, + } + debug("Trying boot configuration %+v", cfg) + if dryrun { + log.Printf("Dry-run, will not actually boot") + } else { + if err := cfg.Boot(); err != nil { + return fmt.Errorf("Failed to boot kernel %s: %v", cfg.Kernel, err) + } + } + return nil +} + +func main() { + flag.Parse() + if *flagGrubMode && *flagKernelPath != "" { + log.Fatal("Options -grub and -kernel are mutually exclusive") + } + if *flagDebug { + debug = log.Printf + } + + // Get all the available block devices + devices, err := storage.GetBlockStats() + if err != nil { + log.Fatal(err) + } + // print partition info + if *flagDebug { + for _, dev := range devices { + log.Printf("Device: %+v", dev) + table, err := storage.GetGPTTable(dev) + if err != nil { + continue + } + log.Printf(" Table: %+v", table) + for _, part := range table.Partitions { + log.Printf(" Partition: %+v\n", part) + if !part.IsEmpty() { + log.Printf(" UUID: %s\n", part.Type.String()) + } + } + } + } + + // TODO boot from EFI system partitions. See storage.FilterEFISystemPartitions + + if *flagGrubMode { + if err := BootGrubMode(devices, *flagBaseMountPoint, *flagDeviceGUID, *flagDryRun, *flagConfigIdx); err != nil { + log.Fatal(err) + } + } else if *flagKernelPath != "" { + if err := BootPathMode(devices, *flagBaseMountPoint, *flagDeviceGUID, *flagDryRun); err != nil { + log.Fatal(err) + } + } else { + log.Fatal("You must specify either -grub or -kernel") } + os.Exit(1) } diff --git a/vendor/github.com/systemboot/systemboot/netboot/main.go b/vendor/github.com/systemboot/systemboot/netboot/main.go index 1da1db9..59d7da7 100644 --- a/vendor/github.com/systemboot/systemboot/netboot/main.go +++ b/vendor/github.com/systemboot/systemboot/netboot/main.go @@ -1,26 +1,35 @@ package main import ( + "crypto/tls" + "crypto/x509" + "errors" "flag" + "fmt" "io/ioutil" "log" + "net" "net/http" "net/url" "os" + "os/exec" "path/filepath" "strings" "time" "github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv6" + "github.com/insomniacslk/dhcp/iana" + "github.com/insomniacslk/dhcp/interfaces" "github.com/insomniacslk/dhcp/netboot" + "github.com/systemboot/systemboot/pkg/crypto" "github.com/u-root/u-root/pkg/kexec" ) var ( useV4 = flag.Bool("4", false, "Get a DHCPv4 lease") useV6 = flag.Bool("6", true, "Get a DHCPv6 lease") - ifname = flag.String("i", "eth0", "Interface to send packets through") + ifname = flag.String("i", "", "Interface to send packets through") dryRun = flag.Bool("dryrun", false, "Do everything except assigning IP addresses, changing DNS, and kexec") doDebug = flag.Bool("d", false, "Print debug output") skipDHCP = flag.Bool("skip-dhcp", false, "Skip DHCP and rely on SLAAC for network configuration. This requires -netboot-url") @@ -28,10 +37,15 @@ var ( readTimeout = flag.Int("timeout", 3, "Read timeout in seconds") dhcpRetries = flag.Int("retries", 3, "Number of times a DHCP request is retried") userClass = flag.String("userclass", "", "Override DHCP User Class option") + caCertFile = flag.String("cacerts", "/etc/cacerts.pem", "CA cert file") + skipCertVerify = flag.Bool("skip-cert-verify", false, "Don't authenticate https certs") + doFix = flag.Bool("fix", false, "Try to run fixmynetboot if netboot fails") ) const ( - interfaceUpTimeout = 30 * time.Second + interfaceUpTimeout = 10 * time.Second + maxHTTPAttempts = 3 + retryInterval = time.Second ) var banner = ` @@ -46,13 +60,13 @@ var banner = ` || || ` +var debug = func(string, ...interface{}) {} func main() { flag.Parse() if *skipDHCP && *overrideNetbootURL == "" { log.Fatal("-skip-dhcp requires -netboot-url") } - debug := func(string, ...interface{}) {} if *doDebug { debug = log.Printf } @@ -61,134 +75,290 @@ func main() { if !*useV6 && !*useV4 { log.Fatal("At least one of DHCPv6 and DHCPv4 is required") } - // DHCPv6 - if *useV6 { - log.Printf("Trying to obtain a DHCPv6 lease on %s", *ifname) - log.Printf("Waiting for network interface %s to come up", *ifname) + + iflist := []net.Interface{} + if *ifname != "" { + var iface *net.Interface + var err error + if iface, err = net.InterfaceByName(*ifname); err != nil { + log.Fatalf("Could not find interface %s: %v", *ifname, err) + } + iflist = append(iflist, *iface) + } else { + var err error + if iflist, err = interfaces.GetNonLoopbackInterfaces(); err != nil { + log.Fatalf("Could not obtain the list of network interfaces: %v", err) + } + } + + for _, iface := range iflist { + log.Printf("Waiting for network interface %s to come up", iface.Name) start := time.Now() - _, err := netboot.IfUp(*ifname, interfaceUpTimeout) + _, err := netboot.IfUp(iface.Name, interfaceUpTimeout) if err != nil { - log.Fatalf("DHCPv6: IfUp failed: %v", err) - } - debug("Interface %s is up after %v", *ifname, time.Since(start)) - var ( - netconf *netboot.NetConf - bootfile string - ) - if *skipDHCP { - log.Print("Skipping DHCP") - } else { - // send a netboot request via DHCP - modifiers := []dhcpv6.Modifier{ - dhcpv6.WithArchType(dhcpv6.EFI_X86_64), - } - if *userClass != "" { - modifiers = append(modifiers, dhcpv6.WithUserClass([]byte(*userClass))) - } - conversation, err := netboot.RequestNetbootv6(*ifname, time.Duration(*readTimeout)*time.Second, *dhcpRetries, modifiers...) - for _, m := range conversation { - debug(m.Summary()) - } - if err != nil { - log.Fatalf("DHCPv6: netboot request for interface %s failed: %v", *ifname, err) - } - // get network configuration and boot file - netconf, bootfile, err = netboot.ConversationToNetconf(conversation) - if err != nil { - log.Fatalf("DHCPv6: failed to extract network configuration for %s: %v", *ifname, err) - } - debug("DHCPv6: network configuration: %+v", netconf) - if !*dryRun { - // Set up IP addresses - log.Printf("DHCPv6: configuring network interface %s", *ifname) - if err = netboot.ConfigureInterface(*ifname, netconf); err != nil { - log.Fatalf("DHCPv6: cannot configure IPv6 addresses on interface %s: %v", *ifname, err) + log.Printf("IfUp failed: %v", err) + continue + } + debug("Interface %s is up after %v", iface.Name, time.Since(start)) + + var dhcp []dhcpFunc + if *useV6 { + dhcp = append(dhcp, dhcp6) + } + if *useV4 { + dhcp = append(dhcp, dhcp4) + } + for _, d := range dhcp { + if err := boot(iface.Name, d); err != nil { + if *doFix { + cmd := exec.Command("fixmynetboot", iface.Name) + log.Printf("Running %s", strings.Join(cmd.Args, " ")) + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + if err := cmd.Run(); err != nil { + log.Printf("Error calling fixmynetboot: %v", err) + log.Print("fixmynetboot failed. Check the above output to manually debug the issue.") + os.Exit(1) + } } - // Set up DNS + log.Printf("Could not boot from %s: %v", iface.Name, err) } - if *overrideNetbootURL != "" { - bootfile = *overrideNetbootURL + } + } + + log.Fatalln("Could not boot from any interfaces") +} + +func retryableNetError(err error) bool { + if err == nil { + return false + } + switch err := err.(type) { + case net.Error: + if err.Timeout() { + return true + } + } + return false +} + +func retryableHTTPError(resp *http.Response) bool { + if resp == nil { + return false + } + if resp.StatusCode == 500 || resp.StatusCode == 502 { + return true + } + return false +} + +func boot(ifname string, dhcp dhcpFunc) error { + var ( + netconf *netboot.NetConf + bootfile string + err error + ) + if *skipDHCP { + log.Print("Skipping DHCP") + } else { + // send a netboot request via DHCP + netconf, bootfile, err = dhcp(ifname) + if err != nil { + return fmt.Errorf("DHCPv6: netboot request for interface %s failed: %v", ifname, err) + } + debug("DHCP: network configuration: %+v", netconf) + if !*dryRun { + log.Printf("DHCP: configuring network interface %s with %v", ifname, netconf) + if err = netboot.ConfigureInterface(ifname, netconf); err != nil { + return fmt.Errorf("DHCP: cannot configure interface %s: %v", ifname, err) } - log.Printf("DHCPv6: boot file for interface %s is %s", *ifname, bootfile) } if *overrideNetbootURL != "" { bootfile = *overrideNetbootURL } - debug("DHCPv6: boot file URL is %s", bootfile) - // check for supported schemes - if !strings.HasPrefix(bootfile, "http://") { - log.Fatal("DHCPv6: can only handle http scheme") - } + log.Printf("DHCP: boot file for interface %s is %s", ifname, bootfile) + } + if *overrideNetbootURL != "" { + bootfile = *overrideNetbootURL + } + debug("DHCP: boot file URL is %s", bootfile) + // check for supported schemes + scheme, err := getScheme(bootfile) + if err != nil { + return fmt.Errorf("DHCP: cannot get scheme from URL: %v", err) + } + if scheme == "" { + return errors.New("DHCP: no valid scheme found in URL") + } + + client, err := getClientForBootfile(bootfile) + if err != nil { + return fmt.Errorf("DHCP: cannot get client for %s: %v", bootfile, err) + } + log.Printf("DHCP: fetching boot file URL: %s", bootfile) - log.Printf("DHCPv6: fetching boot file URL: %s", bootfile) - resp, err := http.Get(bootfile) + var resp *http.Response + for attempt := 0; attempt < maxHTTPAttempts; attempt++ { + log.Printf("netboot: attempt %d for http.Get", attempt+1) + req, err := http.NewRequest(http.MethodGet, bootfile, nil) if err != nil { - log.Fatalf("DHCPv6: http.Get of %s failed: %v", bootfile, err) + return fmt.Errorf("could not build request for %s: %v", bootfile, err) } - // FIXME this will not be called if something fails after this point - defer resp.Body.Close() - if resp.StatusCode != 200 { - log.Fatalf("Status code is not 200 OK: %d", resp.StatusCode) + resp, err = client.Do(req) + if err != nil && retryableNetError(err) || retryableHTTPError(resp) { + time.Sleep(retryInterval) + continue } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Fatalf("DHCPv6: cannot read boot file from the network: %v", err) + if err == nil { + break } - u, err := url.Parse(bootfile) + return fmt.Errorf("DHCP: http.Get of %s failed: %v", bootfile, err) + } + // FIXME this will not be called if something fails after this point + defer resp.Body.Close() + if resp.StatusCode != 200 { + return fmt.Errorf("status code is not 200 OK: %d", resp.StatusCode) + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("DHCP: cannot read boot file from the network: %v", err) + } + crypto.TryMeasureData(crypto.BootConfigPCR, body, bootfile) + u, err := url.Parse(bootfile) + if err != nil { + return fmt.Errorf("DHCP: cannot parse URL %s: %v", bootfile, err) + } + // extract file name component + if strings.HasSuffix(u.Path, "/") { + return fmt.Errorf("invalid file path, cannot end with '/': %s", u.Path) + } + filename := filepath.Base(u.Path) + if filename == "." || filename == "" { + return fmt.Errorf("invalid empty file name extracted from file path %s", u.Path) + } + if err = ioutil.WriteFile(filename, body, 0400); err != nil { + return fmt.Errorf("DHCP: cannot write to file %s: %v", filename, err) + } + debug("DHCP: saved boot file to %s", filename) + if !*dryRun { + log.Printf("DHCP: kexec'ing into %s", filename) + kernel, err := os.OpenFile(filename, os.O_RDONLY, 0) if err != nil { - log.Fatalf("DHCPv6: cannot parse URL %s: %v", bootfile, err) + return fmt.Errorf("DHCP: cannot open file %s: %v", filename, err) } - // extract file name component - if strings.HasSuffix(u.Path, "/") { - log.Fatalf("Invalid file path, cannot end with '/': %s", u.Path) + if err = kexec.FileLoad(kernel, nil /* ramfs */, "" /* cmdline */); err != nil { + return fmt.Errorf("DHCP: kexec.FileLoad failed: %v", err) } - filename := filepath.Base(u.Path) - if filename == "." || filename == "" { - log.Fatalf("Invalid empty file name extracted from file path %s", u.Path) - } - if err = ioutil.WriteFile(filename, body, 0400); err != nil { - log.Fatalf("DHCPv6: cannot write to file %s: %v", filename, err) - } - debug("DHCPv6: saved boot file to %s", filename) - if !*dryRun { - log.Printf("DHCPv6: kexec'ing into %s", filename) - kernel, err := os.OpenFile(filename, os.O_RDONLY, 0) - if err != nil { - log.Fatalf("DHCPv6: cannot open file %s: %v", filename, err) - } - if err = kexec.FileLoad(kernel, nil /* ramfs */, "" /* cmdline */); err != nil { - log.Fatalf("DHCPv6: kexec.FileLoad failed: %v", err) - } - if err = kexec.Reboot(); err != nil { - log.Fatalf("DHCPv6: kexec.Reboot failed: %v", err) - } + if err = kexec.Reboot(); err != nil { + return fmt.Errorf("DHCP: kexec.Reboot failed: %v", err) } } - // DHCPv4 - if *useV4 { - log.Printf("Trying to obtain a DHCPv4 lease on %s", *ifname) - _, err := netboot.IfUp(*ifname, interfaceUpTimeout) - if err != nil { - log.Fatalf("DHCPv4: IfUp failed: %v", err) - } - debug("DHCPv4: interface %s is up", *ifname) - if *skipDHCP { - log.Print("Skipping DHCP") - } else { - log.Print("DHCPv4: sending request") - client := dhcpv4.NewClient() - // TODO add options to request to netboot - conversation, err := client.Exchange(*ifname, nil) - for _, m := range conversation { - debug(m.Summary()) + return nil +} + +func getScheme(urlstring string) (string, error) { + u, err := url.Parse(urlstring) + if err != nil { + return "", err + } + scheme := strings.ToLower(u.Scheme) + if scheme != "http" && scheme != "https" { + return "", fmt.Errorf("URL scheme '%s' must be http or https", scheme) + } + return scheme, nil +} + +func loadCaCerts() (*x509.CertPool, error) { + rootCAs, err := x509.SystemCertPool() + if err != nil { + return nil, err + } + if rootCAs == nil { + debug("certs: rootCAs == nil") + rootCAs = x509.NewCertPool() + } + caCerts, err := ioutil.ReadFile(*caCertFile) + if err != nil { + return nil, fmt.Errorf("could not find cert file '%v' - %v", *caCertFile, err) + } + // TODO: Decide if this should also support compressed certs + // Might be better to have a generic compressed config API + if ok := rootCAs.AppendCertsFromPEM(caCerts); !ok { + debug("Failed to append CA Certs from %s, using system certs only", *caCertFile) + } else { + debug("CA certs appended from PEM") + } + return rootCAs, nil + +} + +func getClientForBootfile(bootfile string) (*http.Client, error) { + var client *http.Client + scheme, err := getScheme(bootfile) + if err != nil { + return nil, err + } + + switch scheme { + case "https": + var config *tls.Config + if *skipCertVerify { + config = &tls.Config{ + InsecureSkipVerify: true, } + } else if *caCertFile != "" { + rootCAs, err := loadCaCerts() if err != nil { - log.Fatalf("DHCPv4: Exchange failed: %v", err) + return nil, err + } + config = &tls.Config{ + RootCAs: rootCAs, } - // TODO configure the network and DNS - // TODO extract the next server and boot file and fetch it - // TODO kexec into the NBP } + tr := &http.Transport{TLSClientConfig: config} + client = &http.Client{Transport: tr} + debug("https client setup (use certs from VPD: %t, skipCertVerify %t)", + *skipCertVerify, *caCertFile != "") + case "http": + client = &http.Client{} + debug("http client setup") + default: + return nil, fmt.Errorf("Scheme %s is unsupported", scheme) + } + return client, nil +} + +type dhcpFunc func(string) (*netboot.NetConf, string, error) + +func dhcp6(ifname string) (*netboot.NetConf, string, error) { + log.Printf("Trying to obtain a DHCPv6 lease on %s", ifname) + modifiers := []dhcpv6.Modifier{ + dhcpv6.WithArchType(iana.EFI_X86_64), + } + if *userClass != "" { + modifiers = append(modifiers, dhcpv6.WithUserClass([]byte(*userClass))) + } + conversation, err := netboot.RequestNetbootv6(ifname, time.Duration(*readTimeout)*time.Second, *dhcpRetries, modifiers...) + for _, m := range conversation { + debug(m.Summary()) } + if err != nil { + return nil, "", fmt.Errorf("DHCPv6: netboot request for interface %s failed: %v", ifname, err) + } + return netboot.ConversationToNetconf(conversation) +} +func dhcp4(ifname string) (*netboot.NetConf, string, error) { + log.Printf("Trying to obtain a DHCPv4 lease on %s", ifname) + var modifiers []dhcpv4.Modifier + if *userClass != "" { + modifiers = append(modifiers, dhcpv4.WithUserClass(*userClass, false)) + } + conversation, err := netboot.RequestNetbootv4(ifname, time.Duration(*readTimeout)*time.Second, *dhcpRetries, modifiers...) + for _, m := range conversation { + debug(m.Summary()) + } + if err != nil { + return nil, "", fmt.Errorf("DHCPv4: netboot request for interface %s failed: %v", ifname, err) + } + return netboot.ConversationToNetconfv4(conversation) } diff --git a/vendor/github.com/systemboot/systemboot/pkg/bootconfig/bootconfig.go b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/bootconfig.go new file mode 100644 index 0000000..c4b9e6e --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/bootconfig.go @@ -0,0 +1,112 @@ +package bootconfig + +import ( + "encoding/json" + "errors" + "fmt" + "log" + "os" + + "github.com/systemboot/systemboot/pkg/crypto" + "github.com/u-root/u-root/pkg/kexec" + "github.com/u-root/u-root/pkg/multiboot" +) + +// BootConfig is a general-purpose boot configuration. It draws some +// characteristics from FIT but it's not compatible with it. It uses +// JSON for interoperability. +type BootConfig struct { + Name string `json:"name,omitempty"` + Kernel string `json:"kernel"` + Initramfs string `json:"initramfs,omitempty"` + KernelArgs string `json:"kernel_args,omitempty"` + DeviceTree string `json:"devicetree,omitempty"` + Multiboot string `json:"multiboot_kernel,omitempty"` + MultibootArgs string `json:"multiboot_args,omitempty"` + Modules []string `json:"multiboot_modules,omitempty"` +} + +// IsValid returns true if a BootConfig object has valid content, and false +// otherwise +func (bc *BootConfig) IsValid() bool { + return (bc.Kernel != "" && bc.Multiboot == "") || (bc.Kernel == "" && bc.Multiboot != "") +} + +// FileNames returns a slice of all filenames in the bootconfig. +func (bc *BootConfig) fileNames() []string { + str := make([]string, 0) + str = append(str, bc.Kernel) + str = append(str, bc.Initramfs) + for _, module := range bc.Modules { + str = append(str, module) + } + return str +} + +func (bc *BootConfig) bytestream() []byte { + b := bc.Name + bc.Kernel + bc.Initramfs + bc.KernelArgs + bc.DeviceTree + bc.Multiboot + bc.MultibootArgs + for _, module := range bc.Modules { + b = b + module + } + return []byte(b) +} + +// Boot tries to boot the kernel with optional initramfs and command line +// options. If a device-tree is specified, that will be used too +func (bc *BootConfig) Boot() error { + crypto.TryMeasureData(crypto.BootConfigPCR, bc.bytestream(), "bootconfig") + crypto.TryMeasureFiles(bc.fileNames()...) + if bc.Kernel != "" { + kernel, err := os.Open(bc.Kernel) + if err != nil { + return err + } + var initramfs *os.File + if bc.Initramfs != "" { + initramfs, err = os.Open(bc.Initramfs) + if err != nil { + return err + } + } + defer func() { + // clean up + if kernel != nil { + if err := kernel.Close(); err != nil { + log.Printf("Error closing kernel file descriptor: %v", err) + } + } + if initramfs != nil { + if err := initramfs.Close(); err != nil { + log.Printf("Error closing initramfs file descriptor: %v", err) + } + } + }() + if err := kexec.FileLoad(kernel, initramfs, bc.KernelArgs); err != nil { + return err + } + } else if bc.Multiboot != "" { + // check multiboot header + if err := multiboot.Probe(bc.Multiboot); err != nil { + log.Printf("Error parsing multiboot header: %v", err) + return err + } + if err := multiboot.Load(true, bc.Multiboot, bc.MultibootArgs, bc.Modules); err != nil { + return fmt.Errorf("kexec.Load() error: %v", err) + } + } + err := kexec.Reboot() + if err == nil { + return errors.New("Unexpectedly returned from Reboot() without error. The system did not reboot") + } + return err +} + +// NewBootConfig parses a boot configuration in JSON format and returns a +// BootConfig object. +func NewBootConfig(data []byte) (*BootConfig, error) { + var bootconfig BootConfig + if err := json.Unmarshal(data, &bootconfig); err != nil { + return nil, err + } + return &bootconfig, nil +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/bootconfig/bootconfig_test.go b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/bootconfig_test.go new file mode 100644 index 0000000..18f1941 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/bootconfig_test.go @@ -0,0 +1,46 @@ +package bootconfig + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewBootConfig(t *testing.T) { + data := []byte(`{ + "name": "some_conf", + "kernel": "/path/to/kernel", + "initramfs": "/path/to/initramfs", + "kernel_args": "init=/bin/bash", + "devicetree": "some data here" +}`) + c, err := NewBootConfig(data) + require.NoError(t, err) + require.Equal(t, "some_conf", c.Name) + require.Equal(t, "/path/to/kernel", c.Kernel) + require.Equal(t, "/path/to/initramfs", c.Initramfs) + require.Equal(t, "init=/bin/bash", c.KernelArgs) + require.Equal(t, "some data here", c.DeviceTree) + require.Equal(t, true, c.IsValid()) +} + +func TestNewBootConfigInvalidJSON(t *testing.T) { + data := []byte(`{ + "name": "broken +}`) + _, err := NewBootConfig(data) + require.Error(t, err) +} + +func TestNewBootConfigMissingKernel(t *testing.T) { + data := []byte(`{ + "name": "some_conf", + "kernel_is_missing": "/path/to/kernel", + "initramfs": "/path/to/initramfs", + "kernel_args": "init=/bin/bash", + "devicetree": "some data here" +}`) + c, err := NewBootConfig(data) + require.NoError(t, err) + require.Equal(t, false, c.IsValid()) +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/bootconfig/manifest.go b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/manifest.go new file mode 100644 index 0000000..4dbd345 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/manifest.go @@ -0,0 +1,48 @@ +package bootconfig + +import ( + "encoding/json" + "fmt" +) + +// global variables +var ( + CurrentManifestVersion = 1 +) + +// Manifest is a list of BootConfig objects. The goal is to provide multiple +// configurations to choose from. +type Manifest struct { + // Version is a positive integer that determines the version of the Manifest + // structure. This will be used when introducing breaking changes in the + // Manifest interface. + Version int `json:"version"` + Configs []BootConfig `json:"configs"` +} + +// NewManifest returns a new empty Manifest structure with the current version +// field populated. +func NewManifest() *Manifest { + return &Manifest{ + Version: CurrentManifestVersion, + } +} + +// ManifestFromBytes parses a manifest configuration, i.e. a list of boot +// configurations, in JSON format and returns a Manifest object. +func ManifestFromBytes(data []byte) (*Manifest, error) { + var manifest Manifest + if err := json.Unmarshal(data, &manifest); err != nil { + return nil, err + } + return &manifest, nil +} + +// GetBootConfig returns the i-th boot configuration from the manifest, or an +// error if an invalid index is passed. +func (mc *Manifest) GetBootConfig(idx int) (*BootConfig, error) { + if idx < 0 || idx >= len(mc.Configs) { + return nil, fmt.Errorf("Invalid index: not in range: %d", idx) + } + return &mc.Configs[idx], nil +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/bootconfig/manifest_test.go b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/manifest_test.go new file mode 100644 index 0000000..7a9e9bc --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/manifest_test.go @@ -0,0 +1,77 @@ +package bootconfig + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewManifest(t *testing.T) { + m := NewManifest() + require.NotNil(t, m) + require.Equal(t, m.Version, CurrentManifestVersion) +} + +func TestManifestFromBytes(t *testing.T) { + data := []byte(`{ + "version": 1, + "configs": [ + { + "name": "some_boot_config", + "kernel": "/path/to/kernel", + "initramfs": "/path/to/initramfs", + "kernel_args": "init=/bin/bash", + "devicetree": "some data here" + } + ] +}`) + m, err := ManifestFromBytes(data) + require.NoError(t, err) + require.Equal(t, 1, len(m.Configs)) +} + +func TestManifestFromBytesInvalid(t *testing.T) { + data := []byte(`{ + "nonexisting": "baaah", + "configs": { + "broken": true + } +}`) + _, err := ManifestFromBytes(data) + require.Error(t, err) +} + +func TestManifestGetBootConfig(t *testing.T) { + data := []byte(`{ + "version": 1, + "configs": [ + { + "name": "some_boot_config", + "kernel": "/path/to/kernel" + } + ] +}`) + m, err := ManifestFromBytes(data) + require.NoError(t, err) + config, err := m.GetBootConfig(0) + require.NoError(t, err) + assert.Equal(t, "some_boot_config", config.Name) + assert.Equal(t, "/path/to/kernel", config.Kernel) +} + +func TestManifestGetBootConfigMissing(t *testing.T) { + data := []byte(`{ + "version": 1, + "configs": [ + { + "name": "some_boot_config", + "kernel": "/path/to/kernel" + } + ] +}`) + m, err := ManifestFromBytes(data) + require.NoError(t, err) + _, err = m.GetBootConfig(1) + require.Error(t, err) +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/bootconfig.zip b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/bootconfig.zip new file mode 100644 index 0000000..63482c2 Binary files /dev/null and b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/bootconfig.zip differ diff --git a/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/bootconfig_signed.zip b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/bootconfig_signed.zip new file mode 100644 index 0000000..3d79bcf Binary files /dev/null and b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/bootconfig_signed.zip differ diff --git a/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/privkey b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/privkey new file mode 100644 index 0000000..2639ecb --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/privkey @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +gMUcPoUXvgI3tpYZ1ZrEXrhCJ970/MOrDpxr5rsUDIT+0FNoCKe/2HTpuC0bCwWa +AOhVmw9EluQQIJfYzkRQFw== +-----END PRIVATE KEY----- diff --git a/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/pubkey b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/pubkey new file mode 100644 index 0000000..3a0b60e --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/testdata/pubkey @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +/tBTaAinv9h06bgtGwsFmgDoVZsPRJbkECCX2M5EUBc= +-----END PUBLIC KEY----- diff --git a/vendor/github.com/systemboot/systemboot/pkg/bootconfig/zipconfig.go b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/zipconfig.go new file mode 100644 index 0000000..c7ca396 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/zipconfig.go @@ -0,0 +1,139 @@ +package bootconfig + +import ( + "archive/zip" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path" + + "github.com/systemboot/systemboot/pkg/crypto" + "golang.org/x/crypto/ed25519" +) + +// memoryZipReader is used to unpack a zip file from a byte sequence in memory. +type memoryZipReader struct { + Content []byte +} + +func (r *memoryZipReader) ReadAt(p []byte, offset int64) (n int, err error) { + cLen := int64(len(r.Content)) + if offset > cLen { + return 0, io.EOF + } + if cLen-offset >= int64(len(p)) { + n = len(p) + err = nil + } else { + err = io.EOF + n = int(int64(cLen) - offset) + } + copy(p, r.Content[offset:int(offset)+n]) + return n, err +} + +// FromZip tries to extract a boot configuration from a ZIP file after verifying +// its signature with the provided public key file. The signature is expected to +// be appended to the ZIP file and have fixed length `ed25519.SignatureSize` . +// The returned string argument is the temporary directory where the files were +// extracted, if successful. +// No decoder (e.g. JSON, ZIP) or other function parsing the input file is called +// before verifying the signature. +func FromZip(filename string, pubkeyfile *string) (*Manifest, string, error) { + // load the whole zip file in memory - we need it anyway for the signature + // matching. + // TODO refuse to read if too big? + data, err := ioutil.ReadFile(filename) + if err != nil { + return nil, "", err + } + crypto.TryMeasureData(crypto.BlobPCR, data, filename) + zipbytes := data + // Load the public key and, if a valid one is specified, match the + // signature. The signature is appended to the ZIP file, and can be present + // or not. A ZIP file is still valid if arbitrary content is appended after + // its end. + if pubkeyfile != nil { + zipbytes = data[:len(data)-ed25519.SignatureSize] + pubkey, err := crypto.LoadPublicKeyFromFile(*pubkeyfile) + if err != nil { + return nil, "", err + } + + // Load the signature. + // The signature is appended to the zip file and has length + // `ed25519.SignatureSize`. We read these bytes from the end of the file and + // treat them as the attached signature. + signature := data[len(data)-ed25519.SignatureSize : len(data)] + if len(signature) != ed25519.SignatureSize { + return nil, "", fmt.Errorf("Short read when reading signature: want %d bytes, got %d", ed25519.SignatureSize, len(signature)) + } + + // Verify the signature against the public key and the zip file bytes + if ok := ed25519.Verify(pubkey, zipbytes, signature); !ok { + return nil, "", fmt.Errorf("Invalid ed25519 signature for file %s", filename) + } + log.Printf("Signature is valid") + } else { + log.Printf("No public key specified, the ZIP file will be unpacked without verification") + } + + // At this point the signature is valid. Unzip the file and decode the boot + // configuration. + r, err := zip.NewReader(&memoryZipReader{Content: zipbytes}, int64(len(zipbytes))) + if err != nil { + return nil, "", err + } + tempDir, err := ioutil.TempDir(os.TempDir(), "bootconfig") + if err != nil { + return nil, "", err + } + log.Printf("Created temporary directory %s", tempDir) + var manifest *Manifest + for _, f := range r.File { + destination := path.Join(tempDir, f.Name) + if len(f.Name) == 0 { + log.Printf("Warning: skipping zero-length file name (flags: %d, mode: %s)", f.Flags, f.Mode()) + continue + } + if f.Name[len(f.Name)-1] == '/' { + // it's a directory, create it + if err := os.MkdirAll(destination, os.ModeDir|os.FileMode(0700)); err != nil { + return nil, "", err + } + log.Printf("Extracted directory %s (flags: %d)", f.Name, f.Flags) + } else { + fd, err := f.Open() + if err != nil { + return nil, "", err + } + buf, err := ioutil.ReadAll(fd) + if err != nil { + return nil, "", err + } + if f.Name == "manifest.json" { + // make sure it's not a duplicate manifest within the ZIP file + // and inform the user otherwise + if manifest != nil { + log.Printf("Warning: duplicate manifest.json found, the last found wins") + } + // parse the Manifest containing the boot configurations + manifest, err = ManifestFromBytes(buf) + if err != nil { + return nil, "", err + } + } + if err := ioutil.WriteFile(destination, buf, f.Mode()); err != nil { + return nil, "", err + } + log.Printf("Extracted file %s (flags: %d, mode: %s)", f.Name, f.Flags, f.Mode()) + } + } + if manifest == nil { + return nil, "", errors.New("No manifest found") + } + return manifest, tempDir, nil +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/bootconfig/zipconfig_test.go b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/zipconfig_test.go new file mode 100644 index 0000000..3c7b310 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/bootconfig/zipconfig_test.go @@ -0,0 +1,101 @@ +package bootconfig + +import ( + "archive/zip" + "io/ioutil" + "log" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +// the sample ZIP file contains the following structure: +// test/ +// test/a +// +// where the file "test/a" contains the ASCII string "blah" +var sampleZIP = []byte("PK\x03\x04\n\x00\x00\x00\x00\x00\xa6\x858M\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x1c\x00test/UT\t\x00\x03\x88\x06\xa9[\x8b\x06\xa9[ux\x0b\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xa6\x858M-2\xc4P\x05\x00\x00\x00\x05\x00\x00\x00\x06\x00\x1c\x00test/aUT\t\x00\x03\x88\x06\xa9[\x88\x06\xa9[ux\x0b\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00blah\nPK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xa6\x858M\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xfdA\x00\x00\x00\x00test/UT\x05\x00\x03\x88\x06\xa9[ux\x0b\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xa6\x858M-2\xc4P\x05\x00\x00\x00\x05\x00\x00\x00\x06\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb4\x81?\x00\x00\x00test/aUT\x05\x00\x03\x88\x06\xa9[ux\x0b\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00\x97\x00\x00\x00\x84\x00\x00\x00\x00\x00") + +func TestMemoryZipReader(t *testing.T) { + r, err := zip.NewReader(&memoryZipReader{Content: sampleZIP}, int64(len(sampleZIP))) + require.NoError(t, err) + // this is ugly, but we expect exactly this sequence of files (and the + // parent directory has to be first) + var numEntries int + for idx, f := range r.File { + numEntries++ + switch idx { + case 0: + require.Equal(t, "test/", f.Name) + case 1: + require.Equal(t, "test/a", f.Name) + fd, err := f.Open() + require.NoError(t, err) + buf, err := ioutil.ReadAll(fd) + require.NoError(t, err) + require.Equal(t, []byte("blah\n"), buf) + } + } + // exactly two entries in the zip file + require.Equal(t, 2, numEntries) +} + +func TestFromZip(t *testing.T) { + manifest, tempdir, err := FromZip("testdata/bootconfig.zip", nil) + defer func() { + if tempdir != "" { + if err := os.RemoveAll(tempdir); err != nil { + log.Printf("Cannot remove temp dir %s: %v", tempdir, err) + } + } + }() + require.NoError(t, err) + require.NotEqual(t, "", tempdir) + require.NotNil(t, manifest) + require.Equal(t, 1, manifest.Version) + require.Equal(t, 1, len(manifest.Configs)) + bc := manifest.Configs[0] + require.Equal(t, "first boot entry", bc.Name) + require.Equal(t, "/path/to/kernel", bc.Kernel) + require.Equal(t, "console=ttyS0", bc.KernelArgs) +} + +func TestFromZipWithSignature(t *testing.T) { + pubkey := "testdata/pubkey" + manifest, tempdir, err := FromZip("testdata/bootconfig_signed.zip", &pubkey) + defer func() { + if tempdir != "" { + if err := os.RemoveAll(tempdir); err != nil { + log.Printf("Cannot remove temp dir %s: %v", tempdir, err) + } + } + }() + require.NoError(t, err) + require.NotEqual(t, "", tempdir) + require.NotNil(t, manifest) + require.Equal(t, 1, manifest.Version) + require.Equal(t, 1, len(manifest.Configs)) + bc := manifest.Configs[0] + require.Equal(t, "boot entry 0", bc.Name) + require.Equal(t, "/path/to/kernel", bc.Kernel) +} + +func TestFromZipWithMissingSignature(t *testing.T) { + pubkey := "testdata/pubkey" + _, tempdir, err := FromZip("testdata/bootconfig.zip", &pubkey) + defer func() { + // called just in case FromZip does not return an error + if tempdir != "" { + if err := os.RemoveAll(tempdir); err != nil { + log.Printf("Cannot remove temp dir %s: %v", tempdir, err) + } + } + }() + require.Error(t, err) +} + +func TestFromZipNoSuchFile(t *testing.T) { + _, _, err := FromZip("testdata/nonexisting_bootconfig.zip", nil) + require.Error(t, err) +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/booter/README.md b/vendor/github.com/systemboot/systemboot/pkg/booter/README.md new file mode 100644 index 0000000..0a632b1 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/booter/README.md @@ -0,0 +1,69 @@ +# Booter package + +The booter package provides a Booter interface that allows to define custom ways +of booting a machine. Booter only requires a method to get its name, and a +method to boot the machine. This interface is suitable to be used by +Systemboot's `uinit`. + +Each custom booter will define their own name (e.g. "netboot") and the custom +logic to boot. For example, a network booter may try to get a network +configuration via DHCPv6, download a boot program, and run it. + +The custom booter also needs to provide a way to be initialized. This is usually +done by defining a function called "New" (e.g. "NewNetBooter"). +This function takes as input a sequence of bytes, representing the booter +configuration, and will return a Booter object, or an error. + +The exact format of the boot configuration is determined by the custom booter, +but there is a general structure that every booter configuration has to provide. +This is discussed in the Booter configuration section below + +## Booter configuration + +A Booter configuration is a JSON file with a simple structure. The requirements +are: + +* the top level object is a map +* the map contains at least a "type" field, with a string value that holds the + booter's name +* the JSON should not be nested. This is recommended for simplicity but is not + strictly required + +For example, the NetBooter configuration can be like the following: + +``` +{ + "type": "netboot", + "method": "", + "mac": "" +} +``` + +where: + +* "type" is required, and its value is always "netboot" (otherwise it's not + recognized as a NetBooter) +* "method" is required, and can be either "dhcpv6", "dhcpv4" or "slaac" +* "mac" is required, and it is the MAC address of the interface that will try + to boot from the network. It has the "aa:bb:cc:dd:ee:ff" format +* "override_url" is optional, unles "method" is "slaac", and it is the URL from + which the booter will try to download the network boot program + + +## Creating a new Booter + +To create a new Booter, the following things are necessary: + +* define a structure for the new booter, that implements the Booter interface + described above. I.e. implement the `TypeName` and `Boot` methods +* define a NewMyBooterName (e.g. "NewLocalBoot") that takes a sequence of bytes + as input, and return a `Booter` or an error if it's an invalid or unknown + configuration. The input byte sequence must contain a valid JSON configuration + for that booter in order to return successfully +* the new booter has to be registered as a supported booter. Just add the + `NewMyBooterName` function (however this function is called) to the + `supportedBooterParsers` in `bootentry.go`. This array is used by + `GetBootEntries` to test a boot configuration against all the available + booters + diff --git a/vendor/github.com/systemboot/systemboot/pkg/booter/bootentry.go b/vendor/github.com/systemboot/systemboot/pkg/booter/bootentry.go index 8414a2c..063ae5f 100644 --- a/vendor/github.com/systemboot/systemboot/pkg/booter/bootentry.go +++ b/vendor/github.com/systemboot/systemboot/pkg/booter/bootentry.go @@ -4,6 +4,7 @@ import ( "fmt" "log" + "github.com/systemboot/systemboot/pkg/crypto" "github.com/systemboot/systemboot/pkg/vpd" ) @@ -25,6 +26,7 @@ type BootEntry struct { var supportedBooterParsers = []func([]byte) (Booter, error){ NewNetBooter, + NewLocalBooter, } // GetBooterFor looks for a supported Booter implementation and returns it, if @@ -59,6 +61,7 @@ func GetBootEntries() []BootEntry { // try the RW entries first value, err := Get(key, false) if err == nil { + crypto.TryMeasureData(crypto.NvramVarsPCR, value, key) bootEntries = append(bootEntries, BootEntry{Name: key, Config: value}) // WARNING WARNING WARNING this means that read-write boot entries // have priority over read-only ones @@ -67,6 +70,7 @@ func GetBootEntries() []BootEntry { // try the RO entries then value, err = Get(key, true) if err == nil { + crypto.TryMeasureData(crypto.NvramVarsPCR, value, key) bootEntries = append(bootEntries, BootEntry{Name: key, Config: value}) } } diff --git a/vendor/github.com/systemboot/systemboot/pkg/booter/localbooter.go b/vendor/github.com/systemboot/systemboot/pkg/booter/localbooter.go new file mode 100644 index 0000000..f0a27f7 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/booter/localbooter.go @@ -0,0 +1,102 @@ +package booter + +import ( + "encoding/json" + "fmt" + "log" + "os" + "os/exec" +) + +// LocalBooter implements the Booter interface for booting from local storage. +type LocalBooter struct { + Type string `json:"type"` + Method string `json:"method"` + DeviceGUID string `json:"device_guid"` + Kernel string `json:"kernel,omitempty"` + KernelArgs string `json:"kernel_args,omitempty"` + Initramfs string `json:"ramfs,omitempty"` +} + +// NewLocalBooter parses a boot entry config and returns a Booter instance, or +// an error if any +func NewLocalBooter(config []byte) (Booter, error) { + /* + The configuration format for a LocalBooter entry is a JSON with the following structure: + + { + "type": "localboot", + "method": "", + "device_guid": "" + "kernel": "", + "kernel_args": "", + "ramfs": "", + } + + `type` is always set to "localboot" + `method` can be either "grub" or "path". + The "grub" method will look for grub.cfg or grub2.cfg on the specified device. + If no device is specified, it will look on all the attached storage devices, + sorted alphabetically as found in /dev. The first grub configuration that is + found is parsed, and kernel, kernel args and ramfs are extracted. Then the + kernel will be kexec'ed. If this fails, the next entry will NOT be tried, + and no other grub configs will be scanned. In case a grub config has no + valid boot entries, it is ignored and the next config will be used tried. + The "path" method requires a device GUID and kernel path to be specified. If + specified, it will also use kernel args and ramfs path. This method will look + for the given kernel on the given device, and will kexec the kernel using the + given, optional, kernel args and ramfs. + `device_guid` is the GUID of the device to look for grub config or kernel and ramfs + `kernel` is the path, relative to the device specified by `device_guid`, of the + kernel to be kexec'ed + `kernel_args` is the optional string of kernel arguments to be passed. + `ramfs` is the path, relative to the device specified by `device_guid`, of the ramfs + to be used for kexec'ing into the target kernel. + */ + log.Printf("Trying LocalBooter...") + log.Printf("Config: %s", string(config)) + lb := LocalBooter{} + if err := json.Unmarshal(config, &lb); err != nil { + return nil, err + } + log.Printf("LocalBooter: %+v", lb) + if lb.Type != "localboot" { + return nil, fmt.Errorf("Wrong type for LocalBooter: %s", lb.Type) + } + // the actual arguments validation is done in `Boot` to avoid duplicate code + return &lb, nil +} + +// Boot will run the boot procedure. In the case of LocalBooter, it will call +// the `localboot` command +func (lb *LocalBooter) Boot() error { + bootcmd := []string{"localboot", "-d"} + // validate arguments + if lb.Method == "grub" { + bootcmd = append(bootcmd, "-grub") + } else if lb.Method == "path" { + bootcmd = append(bootcmd, []string{"-kernel", lb.Kernel}...) + bootcmd = append(bootcmd, []string{"-guid", lb.DeviceGUID}...) + if lb.Initramfs != "" { + bootcmd = append(bootcmd, []string{"-initramfs", lb.Initramfs}...) + } + if lb.KernelArgs != "" { + bootcmd = append(bootcmd, []string{"-cmdline", lb.KernelArgs}...) + } + } else { + return fmt.Errorf("Unknown boot method %s", lb.Method) + } + + log.Printf("Executing command: %v", bootcmd) + cmd := exec.Command(bootcmd[0], bootcmd[1:]...) + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + if err := cmd.Run(); err != nil { + log.Printf("Error executing %v: %v", cmd, err) + } + return nil +} + +// TypeName returns the name of the booter type +func (lb *LocalBooter) TypeName() string { + return lb.Type +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/booter/netbooter.go b/vendor/github.com/systemboot/systemboot/pkg/booter/netbooter.go index 4848b98..bb8ffa7 100644 --- a/vendor/github.com/systemboot/systemboot/pkg/booter/netbooter.go +++ b/vendor/github.com/systemboot/systemboot/pkg/booter/netbooter.go @@ -6,15 +6,18 @@ import ( "log" "os" "os/exec" + "strconv" ) // NetBooter implements the Booter interface for booting over DHCPv6. // See NewNetBooterDHCPv6 for details on the fields. type NetBooter struct { - Type string `json:"type"` - Method string `json:"method"` - MAC string `json:"mac"` - OverrideURL string `json:"override_url,omitempty"` + Type string `json:"type"` + Method string `json:"method"` + MAC string `json:"mac"` + OverrideURL *string `json:"override_url,omitempty"` + Retries *int `json:"retries,omitempty"` + DebugOnFailure bool `json:"debug_on_failure,omitempty"` } // NewNetBooter parses a boot entry config and returns a Booter instance, or an @@ -26,7 +29,9 @@ func NewNetBooter(config []byte) (Booter, error) { // "type": "netboot", // "method": "", // "mac": "", - // "override_url": "" + // "override_url": "", + // "retries": , + // "debug_on_failure": // } // // `type` is always set to "netboot". @@ -35,6 +40,11 @@ func NewNetBooter(config []byte) (Booter, error) { // `override_url` is an optional URL used to override the boot file URL used // to fetch the network boot program. This field becomes mandatory if // `method` is set to "slaac". + // `retries` is the number of times a DHCP request should be retried if + // failed. If unspecified, it will use the underlying `netboot` program's + // default. + // `debug_on_failure` is an optional boolean that will signal a request for + // a debugging attempt if netboot fails. // // An example configuration is: // { @@ -66,13 +76,28 @@ func NewNetBooter(config []byte) (Booter, error) { // `netboot` command func (nb *NetBooter) Boot() error { bootcmd := []string{"netboot", "-d", "-userclass", "linuxboot"} + if nb.OverrideURL != nil { + bootcmd = append(bootcmd, "-netboot-url", *nb.OverrideURL) + } + if nb.Retries != nil { + bootcmd = append(bootcmd, "-retries", strconv.Itoa(*nb.Retries)) + } + if nb.Method == "dhcpv6" { + bootcmd = append(bootcmd, []string{"-6=true", "-4=false"}...) + } else if nb.Method == "dhcpv4" { + bootcmd = append(bootcmd, []string{"-6=false", "-4=true"}...) + } else { + return fmt.Errorf("netboot: unknown method %s", nb.Method) + } + if nb.DebugOnFailure { + bootcmd = append(bootcmd, "-fix") + } log.Printf("Executing command: %v", bootcmd) cmd := exec.Command(bootcmd[0], bootcmd[1:]...) cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr if err := cmd.Run(); err != nil { - log.Printf("Error executing %v: %v", cmd, err) + return fmt.Errorf("Error executing %v: %v", cmd, err) } - // This should be never reached return nil } diff --git a/vendor/github.com/systemboot/systemboot/pkg/checker/checker.go b/vendor/github.com/systemboot/systemboot/pkg/checker/checker.go new file mode 100644 index 0000000..b677fad --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/checker/checker.go @@ -0,0 +1,50 @@ +package checker + +import "fmt" + +// Checker is the type of checking functions +type Checker func() error + +// Remediator is the type of remediation functions +type Remediator func() error + +// Check is a type that implements a netboot check +type Check struct { + Name string + Run Checker + Remediate Remediator + StopOnError bool +} + +// Run runs the checks and remediations from a check list, in order, and prints the +// check and remediation status. +func Run(checklist []Check) error { + for idx, check := range checklist { + fmt.Printf(green("#%d", idx+1)+" Running check '%s'.. ", check.Name) + if checkErr := check.Run(); checkErr != nil { + fmt.Println(red("failed: %v", checkErr)) + if check.Remediate != nil { + fmt.Println(yellow(" -> running remediation")) + if remErr := check.Remediate(); remErr != nil { + fmt.Printf(red(" Remediation for '%s' failed: %v\n", check.Name, remErr)) + if check.StopOnError { + fmt.Println("Exiting") + return remErr + } + } else { + fmt.Printf(" Remediation for '%s' succeeded\n", check.Name) + } + } else { + msg := fmt.Sprintf(" -> no remediation found for %s", check.Name) + if check.StopOnError { + fmt.Println(yellow(msg + ", stop on error requested. Exiting.")) + return checkErr + } + fmt.Println(yellow(msg + ", skipping.")) + } + } else { + fmt.Println(green("OK")) + } + } + return nil +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/checker/colours.go b/vendor/github.com/systemboot/systemboot/pkg/checker/colours.go new file mode 100644 index 0000000..e690a37 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/checker/colours.go @@ -0,0 +1,43 @@ +package checker + +import "fmt" + +// foreground colours +const ( + ColorBlack = "\x1b[0;30m" + ColorRed = "\x1b[0;31m" + ColorGreen = "\x1b[0;32m" + ColorYellow = "\x1b[0;33m" + ColorBlue = "\x1b[0;34m" + ColorMagenta = "\x1b[0;35m" + ColorGrey = "\x1b[0;36m" + ColorNone = "\x1b[0m" +) + +func colorize(col, f string, a ...interface{}) string { + return col + fmt.Sprintf(f, a...) + ColorNone +} + +func red(format string, args ...interface{}) string { + return colorize(ColorRed, format, args...) +} + +func green(format string, args ...interface{}) string { + return colorize(ColorGreen, format, args...) +} + +func yellow(format string, args ...interface{}) string { + return colorize(ColorYellow, format, args...) +} + +func blue(format string, args ...interface{}) string { + return colorize(ColorBlue, format, args...) +} + +func magenta(format string, args ...interface{}) string { + return colorize(ColorMagenta, format, args...) +} + +func grey(format string, args ...interface{}) string { + return colorize(ColorGrey, format, args...) +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/checker/commandexecutor.go b/vendor/github.com/systemboot/systemboot/pkg/checker/commandexecutor.go new file mode 100644 index 0000000..f1218eb --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/checker/commandexecutor.go @@ -0,0 +1,42 @@ +package checker + +import ( + "log" + "os" + "os/exec" +) + +// DefaultShell is used by EmergencyShell +var DefaultShell = "elvish" + +func runCmd(prog string, args ...string) error { + cmd := exec.Command(prog, args...) + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + return cmd.Run() +} + +// CommandExecutor returns a check that runs the provided command and arguments. +func CommandExecutor(prog string, args ...string) Checker { + return func() error { + return runCmd(prog, args...) + } +} + +// CommandExecutorRemediation is like CommandExecutor, but returns a Remediator. +func CommandExecutorRemediation(prog string, args ...string) Remediator { + return func() error { + return runCmd(prog, args...) + } +} + +// EmergencyShell is a remediation that prints the given banner, and then calls +// an emergency shell. +func EmergencyShell(banner string) Remediator { + return func() error { + log.Print(green("Running emergency shell: %s", DefaultShell)) + if banner != "" { + log.Print(banner) + } + return CommandExecutorRemediation(DefaultShell)() + } +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/checker/dmesg.go b/vendor/github.com/systemboot/systemboot/pkg/checker/dmesg.go new file mode 100644 index 0000000..61cd4fa --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/checker/dmesg.go @@ -0,0 +1,33 @@ +package checker + +import ( + "strings" + "syscall" + "unsafe" +) + +// shamelessly copied from u-root/cmds/dmesg +const ( + _SYSLOG_ACTION_READ_ALL = 3 +) + +func getDmesg() (string, error) { + level := uintptr(_SYSLOG_ACTION_READ_ALL) + b := make([]byte, 256*1024) + n, _, err := syscall.Syscall(syscall.SYS_SYSLOG, level, uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))) + if err != 0 { + return "", err + } + return string(b[:n]), nil +} + +func grep(b, pattern string) []string { + lines := strings.Split(b, "\n") + ret := make([]string, 0) + for _, line := range lines { + if strings.Contains(line, pattern) { + ret = append(ret, line) + } + } + return ret +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/checker/interfaces.go b/vendor/github.com/systemboot/systemboot/pkg/checker/interfaces.go new file mode 100644 index 0000000..8e59021 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/checker/interfaces.go @@ -0,0 +1,149 @@ +package checker + +import ( + "errors" + "fmt" + "net" + "time" + + "github.com/insomniacslk/dhcp/netboot" + "github.com/safchain/ethtool" +) + +// InterfaceExists returns a Checker that verifies if an interface is present on +// the system +func InterfaceExists(ifname string) Checker { + return func() error { + _, err := net.InterfaceByName(ifname) + return err + } +} + +func ethStats(ifname string) (*ethtool.EthtoolCmd, error) { + cmd := ethtool.EthtoolCmd{} + _, err := cmd.CmdGet(ifname) + if err != nil { + return nil, err + } + return &cmd, nil +} + +// LinkSpeed checks the link speed, and complains if smaller than `min` +// megabit/s. +func LinkSpeed(ifname string, minSpeed int) Checker { + return func() error { + eth, err := ethStats(ifname) + if err != nil { + return err + } + if int(eth.Speed) < minSpeed { + return fmt.Errorf("link speed %d < %d", eth.Speed, minSpeed) + } + return nil + } +} + +// LinkAutoneg checks if the link auto-negotiation state, and return an error if +// it's not the expected state. +func LinkAutoneg(ifname string, expected bool) Checker { + return func() error { + eth, err := ethStats(ifname) + if err != nil { + return err + } + var want uint8 + if expected == true { + want = 1 + } + if eth.Autoneg != want { + return fmt.Errorf("link autoneg %d; want %d", eth.Autoneg, want) + } + return nil + } +} + +func addresses(ifname string) ([]net.IP, error) { + iface, err := net.InterfaceByName(ifname) + if err != nil { + return nil, err + } + addrs, err := iface.Addrs() + if err != nil { + return nil, err + } + iplist := make([]net.IP, 0) + for _, addr := range addrs { + ipnet, ok := addr.(*net.IPNet) + if !ok { + return nil, errors.New("not a net.IPNet") + } + iplist = append(iplist, ipnet.IP) + } + return iplist, nil +} + +// InterfaceHasLinkLocalAddress returns a Checker that verifies if an interface +// has a configured link-local address. +func InterfaceHasLinkLocalAddress(ifname string) Checker { + return func() error { + addrs, err := addresses(ifname) + if err != nil { + return err + } + for _, addr := range addrs { + if addr.IsLinkLocalUnicast() { + return nil + } + } + return fmt.Errorf("no link local addresses for interface %s", ifname) + } +} + +// InterfaceHasGlobalAddresses returns a Checker that verifies if an interface has +// at least one global address. +func InterfaceHasGlobalAddresses(ifname string) Checker { + return func() error { + addrs, err := addresses(ifname) + if err != nil { + return err + } + for _, addr := range addrs { + if addr.IsGlobalUnicast() { + return nil + } + } + return fmt.Errorf("no unicast global addresses for interface %s", ifname) + } +} + +// InterfaceRemediate returns a Remediator that tries to fix a missing +// interface issue. +func InterfaceRemediate(ifname string) Remediator { + return func() error { + // TODO implement driver loading logic + dmesg, err := getDmesg() + if err != nil { + return fmt.Errorf("cannot read dmesg to look for NIC driver information: %v", err) + } + lines := grep(dmesg, ifname) + if len(lines) == 0 { + return fmt.Errorf("no trace of %s in dmesg", ifname) + } + // TODO should this be returned as a string to the caller? + fmt.Printf(" found %d references to %s in dmesg\n", len(lines), ifname) + return nil + } +} + +// InterfaceCanDoDHCPv6 checks whether DHCPv6 succeeds on an interface, and if +// it has a valid netboot URL. +func InterfaceCanDoDHCPv6(ifname string) Checker { + return func() error { + conv, err := netboot.RequestNetbootv6(ifname, 10*time.Second, 2) + if err != nil { + return err + } + _, _, err = netboot.ConversationToNetconf(conv) + return err + } +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/crypto/measure.go b/vendor/github.com/systemboot/systemboot/pkg/crypto/measure.go new file mode 100644 index 0000000..6538fa1 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/pkg/crypto/measure.go @@ -0,0 +1,49 @@ +package crypto + +import ( + "io/ioutil" + "log" + + "github.com/systemboot/tpmtool/pkg/tpm" +) + +const ( + // BlobPCR type in PCR 7 + BlobPCR uint32 = 7 + // BootConfigPCR type in PCR 8 + BootConfigPCR uint32 = 8 + // ConfigDataPCR type in PCR 8 + ConfigDataPCR uint32 = 8 + // NvramVarsPCR type in PCR 9 + NvramVarsPCR uint32 = 9 +) + +// TryMeasureData measures a byte array with additional information +func TryMeasureData(pcr uint32, data []byte, info string) { + TPMInterface, err := tpm.NewTPM() + if err != nil { + log.Printf("Cannot open TPM: %v", err) + return + } + log.Printf("Measuring blob: %v", info) + TPMInterface.Measure(pcr, data) + TPMInterface.Close() +} + +// TryMeasureFiles measures a variable amount of files +func TryMeasureFiles(files ...string) { + TPMInterface, err := tpm.NewTPM() + if err != nil { + log.Printf("Cannot open TPM: %v", err) + return + } + for _, file := range files { + log.Printf("Measuring file: %v", file) + data, err := ioutil.ReadFile(file) + if err != nil { + continue + } + TPMInterface.Measure(BlobPCR, data) + } + TPMInterface.Close() +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/storage/blockdev.go b/vendor/github.com/systemboot/systemboot/pkg/storage/blockdev.go index cf639c5..b62a882 100644 --- a/vendor/github.com/systemboot/systemboot/pkg/storage/blockdev.go +++ b/vendor/github.com/systemboot/systemboot/pkg/storage/blockdev.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "log" "os" + "path" "path/filepath" "strconv" "strings" @@ -21,8 +22,9 @@ var ( // BlockDev maps a device name to a BlockStat structure for a given block device type BlockDev struct { - Name string - Stat BlockStat + Name string + Stat BlockStat + FsUUID string } // Summary prints a multiline summary of the BlockDev object @@ -71,6 +73,12 @@ type BlockStat struct { InFlight uint64 IOTicks uint64 TimeInQueue uint64 + // Kernel 4.18 added four fields for discard tracking, see + // https://github.com/torvalds/linux/commit/bdca3c87fb7ad1cc61d231d37eb0d8f90d001e0c + DiscardIOs uint64 + DiscardMerges uint64 + DiscardSectors uint64 + DiscardTicks uint64 } // SystemPartitionGUID is the GUID of EFI system partitions @@ -89,8 +97,8 @@ var SystemPartitionGUID = gpt.Guid([...]byte{ func BlockStatFromBytes(buf []byte) (*BlockStat, error) { fields := strings.Fields(string(buf)) // BlockStat has 11 fields - if len(fields) != 11 { - return nil, errors.New("Invalid number of fields") + if len(fields) < 11 { + return nil, fmt.Errorf("BlockStatFromBytes: parsing %q: got %d fields(%q), want at least 11", buf, len(fields), fields) } intfields := make([]uint64, 0) for _, field := range fields { @@ -100,7 +108,7 @@ func BlockStatFromBytes(buf []byte) (*BlockStat, error) { } intfields = append(intfields, v) } - return &BlockStat{ + bs := BlockStat{ ReadIOs: intfields[0], ReadMerges: intfields[1], ReadSectors: intfields[2], @@ -112,7 +120,14 @@ func BlockStatFromBytes(buf []byte) (*BlockStat, error) { InFlight: intfields[8], IOTicks: intfields[9], TimeInQueue: intfields[10], - }, nil + } + if len(fields) >= 15 { + bs.DiscardIOs = intfields[11] + bs.DiscardMerges = intfields[12] + bs.DiscardSectors = intfields[13] + bs.DiscardTicks = intfields[14] + } + return &bs, nil } // GetBlockStats iterates over /sys/class/block entries and returns a list of @@ -152,11 +167,148 @@ func GetBlockStats() ([]BlockDev, error) { if err != nil { return nil, err } - blockdevs = append(blockdevs, BlockDev{Name: devname, Stat: *bstat}) + devpath := path.Join("/dev/", devname) + uuid := getUUID(devpath) + blockdevs = append(blockdevs, BlockDev{Name: devname, Stat: *bstat, FsUUID: uuid}) } return blockdevs, nil } +func getUUID(devpath string) (fsuuid string) { + + fsuuid = tryVFAT(devpath) + if fsuuid != "" { + log.Printf("###### FsUUIS in %s: %s", devpath, fsuuid) + return fsuuid + } + fsuuid = tryEXT4(devpath) + if fsuuid != "" { + log.Printf("###### FsUUIS in %s: %s", devpath, fsuuid) + return fsuuid + } + log.Printf("###### FsUUIS in %s: NONE", devpath) + return "" +} + +//see https://www.nongnu.org/ext2-doc/ext2.html#DISK-ORGANISATION +const ( + EXT2SprblkOff = 1024 // Offset of superblock in partition + EXT2SprblkSize = 512 // Actually 1024 but most of the last byters are reserved + EXT2SprblkMagicOff = 56 // Offset of magic number in suberblock + EXT2SprblkMagicSize = 2 + EXT2SprblkMagic = '\uEF53' // fixed value + EXT2SprblkUUIDOff = 104 // Offset of UUID in superblock + EXT2SprblkUUIDSize = 16 +) + +func tryEXT4(devname string) (uuid string) { + log.Printf("try ext4") + var off int64 + b := make([]byte, 0) + + file, err := os.Open(devname) + if err != nil { + log.Println(err) + return "" + } + defer file.Close() + + fileinfo, err := file.Stat() + if err != nil { + log.Println(err) + return "" + } + fmt.Printf("%s %d\n", fileinfo.Name(), fileinfo.Size()) + + // magic number + b = make([]byte, EXT2SprblkMagicSize) + off = EXT2SprblkOff + EXT2SprblkMagicOff + _, err = file.ReadAt(b, off) + if err != nil { + log.Println(err) + return "" + } + magic := uint16(b[1])<<8 + uint16(b[0]) + fmt.Printf("magic: 0x%x\n", magic) + if magic != EXT2SprblkMagic { + log.Printf("try ext4") + return "" + } + + // filesystem UUID + b = make([]byte, EXT2SprblkUUIDSize) + off = EXT2SprblkOff + EXT2SprblkUUIDOff + _, err = file.ReadAt(b, off) + if err != nil { + fmt.Println(err) + return "" + } + uuid = fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], + b[9], b[10], b[11], b[12], b[13], b[14], b[15]) + fmt.Printf("UUID=%s\n", uuid) + + return uuid +} + +// see https://de.wikipedia.org/wiki/File_Allocation_Table#Aufbau +const ( + FAT32MagicOff = 82 // Offset of magic number + FAT32MagicSize = 8 + FAT32Magic = "FAT32 " // fixed value + FAT32IDOff = 67 // Offset of filesystem-ID / serielnumber. Treated as short filesystem UUID + FAT32IDSize = 4 +) + +func tryVFAT(devname string) (uuid string) { + log.Printf("try vfat") + var off int64 + b := make([]byte, 0) + + file, err := os.Open(devname) + if err != nil { + fmt.Println(err) + return "" + } + defer file.Close() + + fileinfo, err := file.Stat() + if err != nil { + fmt.Println(err) + return "" + } + fmt.Printf("%s %d\n", fileinfo.Name(), fileinfo.Size()) + + // magic number + b = make([]byte, FAT32MagicSize) + off = 0 + FAT32MagicOff + _, err = file.ReadAt(b, off) + if err != nil { + fmt.Println(err) + return "" + } + magic := string(b) + fmt.Printf("magic: %s\n", magic) + if magic != FAT32Magic { + log.Printf("no vfat") + return "" + } + + // filesystem UUID + b = make([]byte, FAT32IDSize) + off = 0 + FAT32IDOff + _, err = file.ReadAt(b, off) + if err != nil { + fmt.Println(err) + return "" + } + uuid = fmt.Sprintf("%02x%02x-%02x%02x", + b[3], b[2], b[1], b[0]) + fmt.Printf("UUID=%s\n", uuid) + + return uuid +} + // GetGPTTable tries to read a GPT table from the block device described by the // passed BlockDev object, and returns a gpt.Table object, or an error if any func GetGPTTable(device BlockDev) (*gpt.Table, error) { @@ -178,7 +330,13 @@ func GetGPTTable(device BlockDev) (*gpt.Table, error) { // FilterEFISystemPartitions returns a list of BlockDev objects whose underlying // block device is a valid EFI system partition, or an error if any func FilterEFISystemPartitions(devices []BlockDev) ([]BlockDev, error) { - esps := make([]BlockDev, 0) + return PartitionsByGUID(devices, SystemPartitionGUID.String()) +} + +// PartitionsByGUID returns a list of BlockDev objects whose underlying +// block device has the given GUID +func PartitionsByGUID(devices []BlockDev, guid string) ([]BlockDev, error) { + partitions := make([]BlockDev, 0) for _, device := range devices { table, err := GetGPTTable(device) if err != nil { @@ -189,12 +347,24 @@ func FilterEFISystemPartitions(devices []BlockDev) ([]BlockDev, error) { if part.IsEmpty() { continue } - if part.Type.String() == SystemPartitionGUID.String() { - esps = append(esps, device) + if part.Type.String() == guid { + partitions = append(partitions, device) } } } - return esps, nil + return partitions, nil +} + +// PartitionsByFsUUID returns a list of BlockDev objects whose underlying +// block device has a filesystem with the given UUID +func PartitionsByFsUUID(devices []BlockDev, fsuuid string) []BlockDev { + partitions := make([]BlockDev, 0) + for _, device := range devices { + if device.FsUUID == fsuuid { + partitions = append(partitions, device) + } + } + return partitions } // GetMountpointByDevice gets the mountpoint by given diff --git a/vendor/github.com/systemboot/systemboot/pkg/storage/blockdev_test.go b/vendor/github.com/systemboot/systemboot/pkg/storage/blockdev_test.go index 4bfb2e8..f031c1b 100644 --- a/vendor/github.com/systemboot/systemboot/pkg/storage/blockdev_test.go +++ b/vendor/github.com/systemboot/systemboot/pkg/storage/blockdev_test.go @@ -22,3 +22,28 @@ func TestFindMountPointValid(t *testing.T) { require.NoError(t, err) require.Equal(t, *mountpoint, "/media/usb") } + +func TestBlockStatFromBytes15Fields(t *testing.T) { + // dummy values, don't judge me + input := []byte(" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14\n") + bs, err := BlockStatFromBytes(input) + require.NoError(t, err) + require.Equal(t, uint64(5), bs.WriteMerges) + require.Equal(t, uint64(14), bs.DiscardTicks) +} + +func TestBlockStatFromBytes11Fields(t *testing.T) { + // dummy values, don't judge me + input := []byte(" 0 1 2 3 4 5 6 7 8 9 10\n") + bs, err := BlockStatFromBytes(input) + require.NoError(t, err) + require.Equal(t, uint64(5), bs.WriteMerges) + require.Equal(t, uint64(0), bs.DiscardTicks) +} + +func TestBlockStatFromBytesNotEnoughFields(t *testing.T) { + // dummy values, don't judge me + input := []byte(" 0 1 2 3 4 5 6 7 8\n") + _, err := BlockStatFromBytes(input) + require.Error(t, err) +} diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_active_0 b/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_active_0 deleted file mode 100644 index c227083..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_active_0 +++ /dev/null @@ -1 +0,0 @@ -0 \ No newline at end of file diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_active_1 b/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_active_1 deleted file mode 100644 index 56a6051..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_active_1 +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_caps_tpm12 b/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_caps_tpm12 deleted file mode 100644 index 4e1f076..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_caps_tpm12 +++ /dev/null @@ -1,3 +0,0 @@ -Manufacturer: 0x53544d20 -TCG version: 1.2 -Firmware version: 8.16 diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_enabled_0 b/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_enabled_0 deleted file mode 100644 index c227083..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_enabled_0 +++ /dev/null @@ -1 +0,0 @@ -0 \ No newline at end of file diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_enabled_1 b/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_enabled_1 deleted file mode 100644 index 56a6051..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_enabled_1 +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_owned_0 b/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_owned_0 deleted file mode 100644 index c227083..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_owned_0 +++ /dev/null @@ -1 +0,0 @@ -0 \ No newline at end of file diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_owned_1 b/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_owned_1 deleted file mode 100644 index 56a6051..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_owned_1 +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_temp_deactivated_0 b/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_temp_deactivated_0 deleted file mode 100644 index c227083..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_temp_deactivated_0 +++ /dev/null @@ -1 +0,0 @@ -0 \ No newline at end of file diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_temp_deactivated_1 b/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_temp_deactivated_1 deleted file mode 100644 index 56a6051..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_temp_deactivated_1 +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_tpm b/vendor/github.com/systemboot/systemboot/pkg/tpm/tests/fake_tpm deleted file mode 100644 index e69de29..0000000 diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm.go b/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm.go deleted file mode 100644 index 6d5442c..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm.go +++ /dev/null @@ -1,193 +0,0 @@ -package tpm - -import ( - "bytes" - "errors" - "fmt" - "io/ioutil" - "strconv" - "strings" - - tspi "github.com/google/go-tpm/tpm" -) - -var ( - // TPMOpener is used to allow unit testing - TPMOpener = tspi.OpenTPM - - // TPMDevice main device path for - // TSS usage - TPMDevice = "/dev/tpm0" - - // TpmCapabilities for selecting tpm spec - TpmCapabilities = "/sys/class/tpm/tpm0/caps" - - // TpmOwnershipState contains owner state - TpmOwnershipState = "/sys/class/tpm/tpm0/owned" - - // TpmActivatedState contains active state - TpmActivatedState = "/sys/class/tpm/tpm0/active" - - // TpmEnabledState contains enabled state - TpmEnabledState = "/sys/class/tpm/tpm0/enabled" - - // TpmTempDeactivatedState contains enabled state - TpmTempDeactivatedState = "/sys/class/tpm/tpm0/temp_deactivated" -) - -const ( - // TPM12 is the TPM 1.2 identifier - TPM12 = "1.2" - // TPM12MaxKeySize is the TPM 1.2 maximum key size - TPM12MaxKeySize = 256 - // TPM20 is the TPM 2.0 identifier - TPM20 = "2.0" -) - -// Manufactures list of TPM vendors -var Manufactures = map[string]string{ - "0x53544d20": "STMicroelectronics", -} - -// TPM is an interface that both TPM1 and TPM2 have to implement. It requires a -// common subset of methods that both TPM versions have to implement. -// Version-specific methods have to be implemented in the relevant object. -type TPM interface { - Info() Info - Summary() string - Version() string - SetupTPM() error - TakeOwnership(ownerPassword string, srkPassword string) error - ClearOwnership(ownerPassword string) error - Measure(pcr uint32, data []byte) error - Close() - ReadPCR(uint32) ([]byte, error) - ReadPubEK(ownerPassword string) ([]byte, error) - SealData(locality byte, pcrs []int, data []byte, srkPassword string) ([]byte, error) - ResealData(locality byte, pcrInfo map[int][]byte, data []byte, srkPassword string) ([]byte, error) - UnsealData(sealed []byte, srkPassword string) ([]byte, error) - ResetLock(ownerPassword string) error -} - -// Info holds information about a TPM device -type Info struct { - Manufacturer string - Specification string - Owned bool - Active bool - Enabled bool - TemporarilyDeactivated bool -} - -func bytesToBool(data []byte) (bool, error) { - s := strings.TrimSuffix(string(data), "\n") - return strconv.ParseBool(s) -} - -// NewTPM gets a new TPM handle struct with -// io fd and specification string -func NewTPM() (TPM, error) { - // It's the caller's responsibility to call TPM.Close() - rwc, err := TPMOpener(TPMDevice) - if err != nil { - return nil, err - } - - tinfo, err := getInfo() - if err != nil { - return nil, err - } - - if tinfo.Specification == TPM12 { - return &TPM1{ - device: rwc, - tpmInfo: *tinfo, - pcrReader: tspi.ReadPCR, - }, nil - } else if tinfo.Specification == TPM20 { - return nil, errors.New("TPM 2.0 not supported yet") - } else if tinfo.Specification == "" { - return nil, fmt.Errorf("Invalid empty TPM specification") - } - return nil, fmt.Errorf("Unknown TPM specification: %s", tinfo.Specification) -} - -// getInfo reads TPM information from various TPM state devices and returns them -// wrapped in an Info structure -func getInfo() (*Info, error) { - caps, err := ioutil.ReadFile(TpmCapabilities) - if err != nil { - return nil, err - } - - ownedBytes, err := ioutil.ReadFile(TpmOwnershipState) - if err != nil { - return nil, err - } - - activeBytes, err := ioutil.ReadFile(TpmActivatedState) - if err != nil { - return nil, err - } - - enabledBytes, err := ioutil.ReadFile(TpmEnabledState) - if err != nil { - return nil, err - } - - tempDeactivatedBytes, err := ioutil.ReadFile(TpmTempDeactivatedState) - if err != nil { - return nil, err - } - - manufacturerPrefix := "Manufacturer: " - var manufacturerID string - for _, lineBytes := range bytes.Split(caps, []byte{'\n'}) { - line := string(lineBytes) - if strings.HasPrefix(line, manufacturerPrefix) { - manufacturerID = line[len(manufacturerPrefix):] - } - } - - manufacturer := Manufactures[manufacturerID] - if manufacturer == "" { - manufacturer = "< unknown >" - } - - specPrefix := "TCG version: " - var tpmVersion string - for _, lineBytes := range bytes.Split(caps, []byte{'\n'}) { - line := string(lineBytes) - if strings.HasPrefix(line, specPrefix) { - tpmVersion = line[len(specPrefix):] - } - } - - owned, err := bytesToBool(ownedBytes) - if err != nil { - return nil, err - } - active, err := bytesToBool(activeBytes) - if err != nil { - return nil, err - } - enabled, err := bytesToBool(enabledBytes) - if err != nil { - return nil, err - } - tempDeactivated, err := bytesToBool(tempDeactivatedBytes) - if err != nil { - return nil, err - } - - tinfo := Info{ - Manufacturer: manufacturer, - Specification: tpmVersion, - Owned: owned, - Active: active, - Enabled: enabled, - TemporarilyDeactivated: tempDeactivated, - } - - return &tinfo, nil -} diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm1.go b/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm1.go deleted file mode 100644 index 62e8469..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm1.go +++ /dev/null @@ -1,210 +0,0 @@ -package tpm - -import ( - "crypto/sha1" - "errors" - "fmt" - "io" - - tspi "github.com/google/go-tpm/tpm" -) - -// TPM1 represents a TPM 1.2 device -type TPM1 struct { - device io.ReadWriteCloser - tpmInfo Info - // the following fields are used for unit testing - // pcrReader emulates go-tpm's ReadPCR - pcrReader func(io.ReadWriter, uint32) ([]byte, error) -} - -const ( - // WellKnownSecret is the 20 bytes zero - WellKnownSecret = "" - // DefaultLocality is the TPM locality mostly used - DefaultLocality byte = 0 -) - -// Info returns the TPMInfo object associated to this TPM device -func (t TPM1) Info() Info { - return t.tpmInfo -} - -// TakeOwnership takes ownership of the TPM. if no password defined use -// WELL_KNOWN_SECRET aka 20 zero bytes. -func (t *TPM1) TakeOwnership(ownerPassword string, srkPassword string) error { - var ownerAuth [20]byte - var srkAuth [20]byte - - if ownerPassword != "" { - ownerAuth = sha1.Sum([]byte(ownerPassword)) - } - - if srkPassword != "" { - srkAuth = sha1.Sum([]byte(srkPassword)) - } - - // This test assumes that the TPM has been cleared using OwnerClear. - pubek, err := tspi.ReadPubEK(t.device) - if err != nil { - return err - } - - return tspi.TakeOwnership(t.device, ownerAuth, srkAuth, pubek) -} - -// Version returns the TPM version -func (t TPM1) Version() string { - return TPM12 -} - -// ClearOwnership clears ownership of the TPM -func (t TPM1) ClearOwnership(ownerPassword string) error { - var ownerAuth [20]byte - - if ownerPassword != "" { - ownerAuth = sha1.Sum([]byte(ownerPassword)) - } - - return tspi.OwnerClear(t.device, ownerAuth) -} - -// SetupTPM enabled, activates and takes -// the ownership of a TPM if it is not in a good -// state -func (t *TPM1) SetupTPM() error { - if t.tpmInfo.Owned && t.tpmInfo.Specification == TPM12 { - _, err := t.ReadPubEK(WellKnownSecret) - if err != nil { - t.ClearOwnership(WellKnownSecret) - return err - } - } - - if !t.tpmInfo.Owned && t.tpmInfo.Enabled { - if err := t.TakeOwnership(WellKnownSecret, WellKnownSecret); err != nil { - return err - } - } - - if !t.tpmInfo.Enabled || !t.tpmInfo.Active || t.tpmInfo.TemporarilyDeactivated { - return errors.New("TPM is not enabled") - } - return nil -} - -// ReadPCR reads the PCR for the given index -func (t *TPM1) ReadPCR(pcr uint32) ([]byte, error) { - data, err := t.pcrReader(t.device, pcr) - if err != nil { - return nil, err - } - - return data, nil -} - -// ReadPubEK reads the Public Endorsement Key part -func (t *TPM1) ReadPubEK(ownerPassword string) ([]byte, error) { - var ownerAuth [20]byte - if ownerPassword != "" { - ownerAuth = sha1.Sum([]byte(ownerPassword)) - } - - ek, err := tspi.OwnerReadPubEK(t.device, ownerAuth) - if err != nil { - return nil, err - } - - return ek, nil -} - -// Measure hashes data and extends it into -// a TPM 1.2 PCR your choice. -func (t *TPM1) Measure(pcr uint32, data []byte) error { - hash := sha1.Sum(data) - - if _, err := tspi.PcrExtend(t.device, pcr, hash); err != nil { - return err - } - - return nil -} - -// SealData seals data at locality with pcrs and srkPassword -func (t *TPM1) SealData(locality byte, pcrs []int, data []byte, srkPassword string) ([]byte, error) { - var srkAuth [20]byte - if srkPassword != "" { - srkAuth = sha1.Sum([]byte(srkPassword)) - } - - sealed, err := tspi.Seal(t.device, locality, pcrs, data, srkAuth[:]) - if err != nil { - return nil, err - } - - return sealed, nil -} - -// ResealData seals data against a given pcrInfo map and srkPassword -// locality: TPM locality, by default zero. -// pcrInfo: A map of 24 entries. The key is the PCR index and the value is -// a hash. -// data: Data which should be sealed against the PCR of pcrInfo. -// srkPassword: The storage root key password of the TPM. -func (t *TPM1) ResealData(locality byte, pcrInfo map[int][]byte, data []byte, srkPassword string) ([]byte, error) { - var srkAuth [20]byte - if srkPassword != "" { - srkAuth = sha1.Sum([]byte(srkPassword)) - } - - sealed, err := tspi.Reseal(t.device, locality, pcrInfo, data, srkAuth[:]) - if err != nil { - return nil, err - } - - return sealed, nil -} - -// UnsealData unseals sealed data with srkPassword -func (t *TPM1) UnsealData(sealed []byte, srkPassword string) ([]byte, error) { - var srkAuth [20]byte - if srkPassword != "" { - srkAuth = sha1.Sum([]byte(srkPassword)) - } - - unsealed, err := tspi.Unseal(t.device, sealed, srkAuth[:]) - if err != nil { - return nil, err - } - return unsealed, err -} - -// ResetLock resets the TPM brute force protection lock -func (t *TPM1) ResetLock(ownerPassword string) error { - var ownerAuth [20]byte - if ownerPassword != "" { - ownerAuth = sha1.Sum([]byte(ownerPassword)) - } - - return tspi.ResetLockValue(t.device, ownerAuth) -} - -// Close tpm device's file descriptor -func (t *TPM1) Close() { - if t.device != nil { - t.device.Close() - t.device = nil - } -} - -// Summary returns a string with formatted TPM information -func (t TPM1) Summary() string { - ret := "" - ret += fmt.Sprintf("TPM Manufacturer: %s\n", t.tpmInfo.Manufacturer) - ret += fmt.Sprintf("TPM spec: %s\n", t.tpmInfo.Specification) - ret += fmt.Sprintf("TPM owned: %t\n", t.tpmInfo.Owned) - ret += fmt.Sprintf("TPM activated: %t\n", t.tpmInfo.Active) - ret += fmt.Sprintf("TPM enabled: %t\n", t.tpmInfo.Enabled) - ret += fmt.Sprintf("TPM temporary deactivated: %t\n", t.tpmInfo.TemporarilyDeactivated) - return ret -} diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm1_test.go b/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm1_test.go deleted file mode 100644 index ee253a8..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm1_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package tpm - -// Manual testing needed or tpm hardware in VM -import ( - "errors" - "io" - "os" - "testing" - - "github.com/stretchr/testify/require" -) - -const ( - testReadPcrIndex uint32 = 23 - testWritePcrIndex uint32 = 16 - testString string = "teststring" -) - -// Beware, TPM testa are a horrible idea because of state transitions -// for PCR. Powercycles are not possible due security architecture. -// Tests must be run as root and follow specific execution flow. -// Also do not run test on a production system if the tpm is used elsewhere. - -func TestTPM1NewTPM(t *testing.T) { - // TODO use a fake TPM. Unfortunately go-tpm checks if it's a device or a - // socket, and fails otherwise. Need to use github.com/stefanberger/swtpm or - // mock go-tpm entirely - TPMOpener = func(string) (io.ReadWriteCloser, error) { - fd, err := os.Open("tests/fake_tpm") - if err != nil { - return nil, err - } - return io.ReadWriteCloser(fd), nil - } - TpmCapabilities = "tests/fake_caps_tpm12" - TpmOwnershipState = "tests/fake_owned_1" - TpmActivatedState = "tests/fake_active_1" - TpmEnabledState = "tests/fake_enabled_1" - TpmTempDeactivatedState = "tests/fake_temp_deactivated_0" - - tpm, err := NewTPM() - require.NoError(t, err) - require.Equal(t, TPM12, tpm.Version()) -} - -func TestTPM1ReadPcr(t *testing.T) { - tpm, err := NewTPM() - tpm.(*TPM1).pcrReader = func(io.ReadWriter, uint32) ([]byte, error) { - return make([]byte, 20), nil - } - require.NoError(t, err) - pcrData, err := tpm.ReadPCR(testReadPcrIndex) - require.NoError(t, err) - require.Equal(t, pcrData, make([]byte, 20)) -} - -func TestTPM1ReadPcrError(t *testing.T) { - tpm, err := NewTPM() - tpm.(*TPM1).pcrReader = func(io.ReadWriter, uint32) ([]byte, error) { - return nil, errors.New("Fake error") - } - require.NoError(t, err) - _, err = tpm.ReadPCR(testReadPcrIndex) - require.Error(t, err) -} - -/* -func TestMeasureTPM1(t *testing.T) { - oldPcrValue, err := ReadPcrTPM1(testWritePcrIndex) - require.NoError(t, err) - - pcrValue := sha1.Sum([]byte(testString)) - finalPcr := sha1.Sum(append(oldPcrValue, pcrValue[:]...)) - - err = MeasureTPM1(testWritePcrIndex, []byte(testString)) - require.NoError(t, err) - - newPcrValue, err := ReadPcrTPM1(testWritePcrIndex) - require.NoError(t, err) - - require.Equal(t, finalPcr[:], newPcrValue) -} - -func TestOwnerClearTPM1(t *testing.T) { - err := OwnerClearTPM1("keins") - require.NoError(t, err) -} - -func TestTakeOwnershipTPM1(t *testing.T) { - err := TakeOwnershipTPM1("", "") - require.NoError(t, err) -} -*/ diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm2.go b/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm2.go deleted file mode 100644 index 585038a..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm2.go +++ /dev/null @@ -1 +0,0 @@ -package tpm diff --git a/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm2_test.go b/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm2_test.go deleted file mode 100644 index 585038a..0000000 --- a/vendor/github.com/systemboot/systemboot/pkg/tpm/tpm2_test.go +++ /dev/null @@ -1 +0,0 @@ -package tpm diff --git a/vendor/github.com/systemboot/systemboot/pkg/vpd/vpd.go b/vendor/github.com/systemboot/systemboot/pkg/vpd/vpd.go index d46c11f..025231d 100644 --- a/vendor/github.com/systemboot/systemboot/pkg/vpd/vpd.go +++ b/vendor/github.com/systemboot/systemboot/pkg/vpd/vpd.go @@ -10,7 +10,8 @@ import ( // VpdDir points to the base directory where the VPD sysfs interface is located. // It is an exported variable to allow for testing var ( - VpdDir = "/sys/firmware/vpd" + VpdDir = "/sys/firmware/vpd" + MaxBootEntry = 9999 ) func getBaseDir(readOnly bool) string { diff --git a/vendor/github.com/systemboot/systemboot/resources/LinuxBoot.png b/vendor/github.com/systemboot/systemboot/resources/LinuxBoot.png new file mode 100644 index 0000000..d6e6440 Binary files /dev/null and b/vendor/github.com/systemboot/systemboot/resources/LinuxBoot.png differ diff --git a/vendor/github.com/systemboot/systemboot/resources/LinuxBoot.xml b/vendor/github.com/systemboot/systemboot/resources/LinuxBoot.xml new file mode 100644 index 0000000..56947d8 --- /dev/null +++ b/vendor/github.com/systemboot/systemboot/resources/LinuxBoot.xml @@ -0,0 +1 @@ +5LxXt7NKki36a+qxe+ARj8IKL6yAN6zwHgT8+pOp9e2yu7qq++4zbo97v6qtJXxmRsSMOSMT/QnnukOa47HUhyxv/4Qh2fEnnP8ThqEohoM/cM/5s4fCiJ8d77nKfp30lx1OdeW/diK/9m5Vli9/c+I6DO1ajX+7Mx36Pk/Xv9kXz/Pw+dvTiqH926eO8Tv/hx1OGre/7f1P8i/7X1W2lr/1jGL+cuCRV+/y18NvGPVzIInT5j0PW//riX/C8OL77+dwF/92r19dXco4Gz5/tQsX/oRz8zCsP9+6g8tbOLq/Ddxv163nb639E86Wa9eCDRR8/R4W/8nF6L9zMejcnPfrXz/un90Pj1G6QNOcJBEUI7P4P349YI/b7bcHYFQL7sUm4MsbfnHOZc07cJJYzd0HPAp8HXo4dLDTGGKb+m/XgAf/+bJ/aPmnrNbcGeMUbn+AI/5tV/48sHCjGPr1l5uBHoHtqm25oR3m763wgoT/gxet89Dkf3WE+v7785HffAEOWxYvZZ79ekAbJ3n7HJZqrUBfcD4FA5iDe7B7Pq8V8Czt707oqiyDPWHjtnr/7hX3XwfWAfbs61J/fty7jRfo48jv2uw3I4Ab5cdf7fplQykfunydT3DKr6PEb171K17pX5ufv7g+/Vtsln/l9Tjza2f8K+Def771X5wGfPnlN/+uD+H/YOs8A/H5azNvk+Ej/GXHXw0N8ncusMbzeod4AHb0Q5//tk+sYGu+p9f5up6/XCPe1gHsGua1HN5DD4w2wLH/ew9Cb2D72wqw8Zu1/itDLMM2p/k/6S32C9/i+Z2v/+ScX+gJR+G/NOect/Fa7X+LbX+obbB/Hd9a1W/H70fwrzPnf74ny/cqhYiQzaAf8/InDLQR6fP1M8zN1ycAvn53/v2V3Qbyw39sC4ghALPfjTVemqp/w5Hr92oe+g5a5nda8ccgy9cv/vvI8u/E+x8Q49TfxThK0v8Q5Bj5O0GOUX9AkBO/4zf/RYz/JQT/rUD/d4P4fxahf3j0/br0OVTQHZE/0yb6by2E3ei/vccPRvy67O/G/s/t+J/F9e/Z5+/ievuPLyvBxOWbwH+y9T+L6P867mGULt/4+iF2C+wkYEw/HKAd4gxG8f+yOP3DI5JBsH8Zkejt9n8pIvF/bXH3JydhiOn8u3j+v8hCv4+rf4Dd/jFQ8X/kS78LpSj2f4Mv/Y4k+P8wX7r9G3wJ+7+C2GBk4vOvThghEC9/NBLf/nVcpsOc/zX8/ndJ1lK1VfqVXD+gy9t3HTapB+IEyJEr/mqU/1+wIoL616zoz1H7h2Mw/a9ZERjWEX6tum/Ngv3+vS/jT9kDRmT820ZRHXBo/qnkTIZ1Hbp/Ora/nsCX6wrLLHfYC0zcRpiO//NTNVWXZ1X8n8MMCLUIt0e4Db6nQ9cNPQgEcS23DniiCPfGBfhwt+M/R8jARQwY6viP37b/GEbL3H6r0/wGxMw/AjGD/w4Oo3+A8X4vUP9fM94/Lw/8AWaFJk2gOTlgrGH5jxag/X+0cDj/c9n/IGOi6O3vkir1O/oE/51IpOn/57ZEf0/X/i82ZhavMbDlzyYmfkOMq3zWtD+IKr2HO/hnOF4peG/wTVzgNsPdQ/CHB70PcriD7RTNRizoER/QQfH+/BPGvvc4d+FRLmDlV6CDbwvc1oSPcO/GT/i9ErcV1/MexHvhhUrW8ILpfg6goy+WiIUxa/Lyt+z+88/ClTYM7DbiWDwO7EEWMsX12UbrvAc4/L4LrWD5NoH12C2P7CLCe5B/2KDfwee641iZA1XgLy9XxRJJT2ijJCRZdsOP1qRKbTlx1guN7MRm5XJRo/PRtX3M3nQy+dDJfnvnlu447Jv4vBJZ57SHdClN5UUOGS7x2fqtb9Poiynonu5rfN9VorlnHpVN++ijcIDBfyxNw/G5LpC4ANLdeqTQexfsMYS79kid96tiKqx+CSGljebOMA8UDzhdsj4Sexfu0dQJlsVWk2qdL/vzuKd323/I0WwgZomcnXXGYydG4hSNRN/kof122DuIVJZQlZEb6b4JzmF6T3fiqrwT8+6avnIo7nlKMSafi/0ENpPpqOiL2YZ5tiXKlvOK0h53JiJgjcdz3taqq6JSkpRIHOJyiIep7B7v3KZg57BnYkzTJA8OQfaYk9PGKiG467GW6L7qIJBuH0m9D1wXqQcupJ+aFXzf93ybF4xDAiMSdSPHaVqkGe3SVneZ1OVePG46O7lunMofRSk0IQzLeVuqkhuh11LDtG/gSw3HlVKHEoxTTM9D18TSsSSdGivztjXIO179gdHHga9NrlLlMvM82yEJQzTUvQIXwxvssBuCdCeRl0Zfb3PyHVW/X7ZxRw3kw1uGDD41cZUAbotXyn6ecujesPMu6wLiyrFGu8Sl3q6TPM0WkSpJCPg74LWBFvEagB0Wxzmdsz0LudzR2FdiB9DIbhMNH04TCCcCc4fqXX+6rfR+lx7riuJWFE+5LSW90cBZGvmU5FCWc7uX5Oi0vMOojyK1mLRWweEezbsqLuGI0KwqPoxxFVHcT1HS05KjhJgsceWu9oNZAVNtsvScWq/ayfdHun9kJVAJbcCAh9la7hzghh89PSjRmTJ+Z4DNStyi6nyjUPJjbRF/cC7w3dt2qqyjwbbNoB3cXUGCkHlaHPucOjyxhPvURvKrf50hiTdDVanGwd/1BLgHaNu4h90RuXL0qbkGADy7sI2OdnebnYFdxOdZn5f26f0sfmHK6HjJkC/QTt5hah2JrKpsUIFFTeqkktfepHVPQdZATVMzBwTemgWnj9C40ADdFNsnhIUPPIePXR6PNWM/HLVwX8CpJTl+2294ojqKfsSY3apH+UxkY9drrHAUFfD1kkdkmdf1N+ip50e+JN4ddtWdWXOPMwkl5KjvOump1Fwu0Qs/GeBDx5NjIBAt70yJ0xu48oaTxeZnejUwLmxxTN1O31Ut+W3Z9mPINyfhHZMn8X7YBsJ69/H+puSBUaFZVskAn5ls+xaBbua9gmjAiDbS6xhoz6SOpkppKLFXbxMlNxMxx1Eyk+X9uAhsUeuZIpZx5CwZOOvj87zHfKwKDhx9h9KAYxB1ZRbm/c1p94ffnYejr57vIds0kY+iDVyRnzJxnLrTfwP8El9A34dRiUgfbCyENvjgfMMLc2qjSi28XyYuPk6IgQIZrvvp4yoZaOFaofhzfCqDOk2M4peSKE9+XAcUCUAGnk2TD5ncVmSzclahPmn44lFyuXVGcGBCoOeBfX7I9xi1TYr3b/0dvybkZRO3nA89K3iAOzBMiNtIJqlD9dbZGtE/dN29IzUM7VJaOZZVWSb1HEFntfbGwsIULl+3nVXRTkAQlABYhWwtoguou412qr3Tt+am5OU/9Koh5ZnJXd1BDyOK2tOubDB8D+uOZGT42fVbYOT+c7nwIuh0fez0wjR+c8PSE+6yBTlR0V65Az3Sa4j+zrw+cXzwaUeTqAmdtnkmwuUqykudOZXMAVyqdqw1TkDLxfZDw2CjQcYW+VslpAcm3gr5TBK8oQ0GRZrjnT8GmgmHbDGGtX25UnWuzzWrHnkpfLo3yt8z9/D8tMahE+Z1A57akkj8g1eYUAmUC/BWh02P/NQL5M4AWED5T3c9A+AqCOBUgmBoamlz4dh3hhxW6OF3ZExGLQFbyWbm25Z6ru1ocX2dpLwyS8sLpgrCLidRYWt/kOt9LgEs+7NDZ75oLVqxjwW2Jm1iCbzxQGSP7OUbMhibN6UN9ofI6PqiKNXsxFtusk+h8XTmlOuKTL9PZTY8OW4ITh+SEgZVXmKPCRxQiEf40qJ+tgXksts3aIA3Cod0T5jgzWQ1dX7QcEyrhvN1RzxcEO0AtF0LGbjYUrMZj8nKEjpOwG5MGh2VZ7qhwiuRaXNmSNRyeE5TXW1ZiD3j2opbYWjfFfcOOFpTlTN9363S1gWQVnxDAh7b+TbCKOo73al1wdIcsuTrgBBXdNr4UCKeR1WpEoPeEkoilihxzEQTV/XCu9WIWn5MOtvofl3b7em+oBELacPx1ELc1zlrJ7Lqk8XuG/Ow3Dhp8/fEq8Y9XzzUfhKA/kQrjKWJO31n6UTvsMXPzCDmgHWxW5kpgdLOFfZ3G62yXKrY56XYgZ8p5G44rWtBrwmsLEKdKXXv1CpOq38HqTV89B718IEuT93yDrkMYBRqGcq3aG5vjRfPDi2m8oiqBLhDk8fnNpNpmufUBXp+bEt/V2u/rPRGppR7K6wPm44s1eC3LBCYDonnN+BJoryKDx0Y9vVxyppO1g6vW5weHyuNrXOuuPxkDNMm9ENm+f6VNG0cKqIE3FaZzJGeERhEeMgcS1iWUi84pKmFfX+27CuhdwccPAHmjciOpn1I9Pt6ntrH4eNYE29UqKQ0Z4dSGLE7BPfe52Y75t0mdargpFgBe7nVTD+jWf9MArbwjupB7xdpXbhjdFyXl43oa8ONY8+mKJbn2zSi+/P5hEA8+NNOLB6zo/Yiy1GV95KyJW/SRaiggKNu065wmmkxGwYjor336T5UYiDUuc8fnaUCJ/682PtAP9hHUhTXTMI2MhUaU4BKHayIAN6SikjvDpzFkBuxfBHAvaZMiYqncINwZgDHBQnWq90y4nIvVpL7mSatEgjL3YqUQfFZP+Gup3M5ljtl7Tja6vhSNS7t+xtmnVIyZ9oYQo6bsc0dBVlVfrIWRxLv+u3ZsWGpJi/54/MJcAC5M6ic4Phx/MDaOcd5nj+1FyvrT/ZtqqWlahWx6rXHWIBpRNMaWSD/vvZQHLux6pwktkhIBMmVrKIul0wTbJHvIsCyNvPbDFo7f+QGabskooyvDm9L4JfutuV5Rhs4zayMMt5w6JFJrapC1UdsPFD8bIYyW45bi5mrhivlkkEcue+i9Ea7w1JZMKQjgHdxD49KYvTNJJCyduhDuAMWd8gvvFg0AvI3ANIAv0rmYQPqAPSNkwD+p92e90/txfw7D95sTfGfkCsHkJc+2P0jCVXR3tBORkhRx16VHFrv4Cw/xB4E34FixwS2mDYftB0/vCBg3PKqQ2qI7beNqwvuiS//kW07bMAVLZh9eVFIGFf2mJmMB4kdS7JZwNYhXjvUnf1EISc5BBgq9mcUx93YluGm258kLHWl0S2Wd2IJffMlCrDVdDaI8WQF3YvOrNo32BExAd5oqHKHQOxNrTwY9XvE3EOndZKTgLxsd2M2op1/l66UVx65LJuEg/hcHPEGvaW5C2Vza98YHQmKgW7iTf+cZnElgNYPZkP1ZQFGim7TtpFK3ekVu/MB0xfeEQvbsagiB0S9eBys8YQ0spfsHgS6pSwY5Yeaph6LJ7Veo9iiAexwNNnDeRWnFzftkZXRukSLJh1WXABmipreCVhMUGwP1awHxp6MqG+xgRRhJmtvy6HXGkcWrfYS4yLuPVn2NJV0JiYYMmxDsONlOl2H0VVQFKmWTq06AnSRSRF13SU3BeeD9E52WcbO3oFaRxV3lNL7sp2nGVzmQ4p7zC3OZJLJdO8XCUsCkMcAp9Lfcr0Z1uzHhyZvRm1hEfJWmvRqzs4+ZgZs3yXvyoOeJfW3IjtvwPVduohIghia4uFCm4E7GZg61HfmmWIjgqVD6wwHTYmWNoL2z2ujl/0LZYrI4FsWeEwdYiZfh4gRv9qaoPRF4863Wg6ucDydCCaudXaFkDctvr3tLWfktDa2eje8RNu7XMtIjXEKO8V5lbE9zxS1e/HyhDN5X9Ntz+D1Il5d7FSUu5UkplN3H+C4BGlSSNNbSDnnlqX+pKWbiihOp4d1NkMlRQnT0J3eB4IxdPY3DkPu9jrUPOLlSCpOTlbA/yWJfNe8kLoHp4QKArFSZ9mTYbTRMNq0Xnh4YQ64Nxgc6qnROIJ0mQHYQoYS2LHLUjU1nMd6pOE60r2NPD8DEAxcyCuLhiS3oo+cE3SxvhsCmpvP+41hjov/qKI39cJtl086iBOXS3j4/XN78onx0/3ep3bIgS7gftNti4EEddReU3J4vJxGnxulp+u/fDRrVOB+xtyTZI2nxb6vwaPIMku2nec7gPShGdazbtRDuU/qMDkO4HaiMnfbODquqSTW1WnkrW4wo45htnEO6NkOUFuzvBgCVmOQAkq60H2/YXWHdROaDyiisc4Ua7MJfPcR7J/lm7fdKJGgEcCdzmhU7hsdAzHHy+e9+pY+TEBEXGgVvu4vWABpMU2sXQE1dDfEypKzd/qGGEiKdVgAMjlqW50onunTQUptzJQRYM80tmQexIDH4A+e530Bk1TTULh+Zk4E0cBY3fbmNnbACXymTnvVWRPf96bGw19EYIc9UeMY4EYyje+C2aRtKwNtRpCfunfo9+ZQcZjzgvPinNxJWNMT3j5pFCDGhHbEmrl8II5vzmjCriYzK9YgoWvykpmLdVp15VdxWKW4A8ErbklP3+C4gP6iKo0XZEjC2o9XiaNvWLHp5izVKS1SSJpjKOvqijrgHbfTvDZekBXIpvTrfrgequv8hzkI/c4+enqQrZi/WyDksKvYR7w9HF8CEmc02pXol2KiqrFTmwA4jQGElVB46u7HvAVgzV8AMOsu5PWu03hb+qJhbaj2kATk/KjzW9aqgH9NQpjW6lmqGwRQCevdlThIFF/2XZKQl8cKnk697pbiHsq7N9C74wNKbbKaLakEWsmHt/YyKSvBQRmRWSS0m5ABL3brdcSAH43unQSh9E6UrV3aLRDtiCTJEfrm8/GTzyC2wfxfQG2556GI6PkTIM1n0TI9YViA1ZXgLZpDHea12AlQCgS1xeSHzkhqnLzu1ZKFQgaqXocnuIi7A4/Qa4JxJ8YL+Xtky4N3fwL+8ww+e2RsWBJX01mue98lj3XbP5zdIlCCOsvbcWSXM1RYqdmBKGv1spEPh0ZzhSy0wXnbL6bemUt0VVeNeHWbITRDj4c8th5UWYDVUZnFZmSPDa1mmHYjCILCqxuLeorndWPCMYiCBHGRxcjesCZHyEnowwhedWUwJzV840HNcKOvqjItWRLvsviIYjyToZI9xB8wAt7Y3OZtddQoCXf8h0AFKYAd5ipKSSLMTzf1Zf6JM73z1YeV8FHrtNiOIm0vXzTH4Yj5uGidMt8D7HmxkZkfBJh/pqNSLWkd6VhO0PM8j9xGF8/mW0Y5gXBgHptfcj71JBAJxWMtSnCNnVfc8s/brk3qJEXlZIex7VziGQJq+dKCynSuOIhqfvAAQi3qKR/5gWydc70AK0KZjsripcESVdwli3GzZ1OC4RRvcl7k+WD+moBY9gXH52h8PH1PfEIdkdZJu2vjzi/PMfEPOsqWtTlDp64QSG6dhA8sfWz0n7YXMGk8E++FGitMFIPyqQzhc7n2W3Ucon1uAxwJCFUa/AxADv/x0E+I7hjl6bwFskFL9YDiKGA3VwkyxzVftHQAHx4NdvbKuXCTOSXhqIYL/EyWMBCPJjceNa2M8TC8ObEsgldQ7D0iWB53YS8by8ZYWQLh7KwrSLLdJFLzoUa+ClFm7ejY5aYAMuUoRevihYC8qpNYhb4mAAyNU3oqjKMOKADfavy7yw8nyNTiUU1iIuqcjKNfj1bZGNhoVoOgyI5cq9LlIFnKMQxZSBBzMfOnOs4Qu+8O4I7NFdE1SQfrwZ7kdp2xq34EmLbuFPYEWXj6EJWtW4pvyhRI8zKRDDCnlYUF8l3UK6dXCnAI8VuGygz/1THep2adWqCewV5cjnMyuDli5XHQNL/elH15ZhkDExfGkNgd3zFafSR0LhP8hmIdLLmUYeac1rT74Q6OJoAD+yRFpRAqQK7Nn48NjO3rAgn6ReEO7ce27/tB37fia0KliZ7TzTSMS0lh+tJQFGV6nX1/9BJo/VGyhWB8lQogL0dmi7KvtSd1RNn6ynTjfRBEhgxZnDROr03Eol8EehaXzgsG71uTKIxSKbo1eowg9zXeEeLg5j4GBPuDfr6BQU3ucV00rOzuA9V6k/iqX9xUwBIkYLPl9E6ypF5jxODsj8u6qvmIngO9DttmT4eivp9D8dx/6puYLFbIyyHub05Qbk8A7fkd9PT1VP2XOMRyBVLn6wcZ2PIBSfEMVDxza/YANazLJYxTfIVEbt4okoPgI4+G4qmlr46+JsjRu9sD6okmPvoE8SOK+sOS73S0Y7dEL61NrwfSUVlblpxY8DjKx5Kx5a5iwZrXjOWaUNlPYoflJfS1vLDEm3x9RLeW6BQVPH0U38uSOMilJkZ3dq+TAcEaA/tUtX06U6AeagyEdGSz1bdeFth8ZHjmgMPaOXDtemUHQ+pwGHsnkVTZo54vVYb9ZUSLSHNFsGD9Y0DfyxBWFpIJEwx6OmE+gZa1EgJCf2nfw1BVd1lephrBxsftO7N1P25yGn3YbpIH5jHw5+ZV75t0tybh3bmHWBUXlEDPN7GXX936YhgVPXcDr5vGy8LAR4BK2MXIEVhVu1C07s0df5np3Av3z+32UclsY6ojRAq9bXskL7H4cudeI48vpyyKHXM814X5OApfLaByZi03jom8KlRHMFdrKxQxS6wbp+hThKAtYmn5JpD2rZqh0xrOli9FXbRWZ2TtaD4ib04CULFdYZPqH56Pdrw56iJEEaWBma6nCp9e+ZmJy1eJ2ZxxvCFmfg0Pw1c3H/UkWwqvkTFAmroyhVsTIO/JyJtdWh3neL/5x/J6IgJaHKKFlKjyNuttysKtP22vyxX+PjieaXQqA8cHCpHlXdfMdeI53GgJVGqwb53jJJPBeI6C7orE4N7rQw0PMHaJMsXEIDweHb/jM97c0Pblk5lCLC7QuIVyQPPuc4wd4S01YW0YzgjNKgZC8XO7zNrSVXpe5vNWC+BMedy2xqt66bhlUYDCuTfIsgsl2s2zVbLBdUIAYHAYlDikeM/UCAnp7OSGh4+R6pZAjAADeBgMQ5XNt4qnHJb+8NxCbUNY1xTL2EnfDFuOStIu0DNP+/hh/UyUJI8GHYAYt9MHstvNPTKM4M4gRnRP1OU5+BW/By7UIyNPOabLLddAcYBJj2iddMT2YG/wkTe1ri+aAjBzu3kAaJiOj2PgMz5WfrOSfb4coF2qFGStUQ9jYfGqDn3NZwMZ+a286w++vQ+k1v80bESH1+i++A8qdg/cHRtk0Sqqoq4BCHFAtfEWsk8FIER4/+zPG9DJFeZeZtq4Gb1mmYg9Ga4aRQU1YZUXT1baPNaGlB2tyGRABpZEwq6dlzn50S+ouDf62qb2mehydRIeoId7PfnQ8dUaZSguFN7JK6zURUHDfSjPd5gdlOjjGFfvy8MjVfu6LnzP28s19JaO6VESZMtSa5Eh0l04diPNZ2Qjdh/AitYH1G7DmxfF4ovFE3cXoEk+edBEBy6Ekt2XMQpLKxKgXlWRga+ErN+YzMbzIiZFyo3VXDogqN1hqXpU7F1NKOhOqPyWxzdKURn++HOh/gU5O3p7whAbPaoI8wJHe/4gm2ZqP5vbKPJULRqU0NraSiBNRS+/L8sWA+neir3m3pQSuyTy6be2v2BYNBWytg3CbQwoPoozY10z0VWmR+8dSzSu++6pJHBjMicz8fGAuBlFsET2dGcpb0pZ+lz3D8otTuncZeqZJIXJlzDdPscLP52Hu2HeRHjGecw33UeMHRDR7lWqLCykw/vxwPlb3Z3H+0WssnqZrUyG+0U+NPL1RqT35wm1tDhM+e3Ho3L8ASy9sDssXd8Ku328Gy/NhWjDky2boRNnOAErE2ICOd+jhjESkI/SURkbfVjaMR9RpNWhxdYEp8uXvNVwddASApVEIHi4vanjpXoWB3xVcPpjLyLMKN84TTIBELxdFoCsOPth3F5xUiRNVWsRLQMuh5ZdlVxwctccpmUOMbbK3CH98FpL3BzV7ah4RPXhxqSXW5g6nL85HNv2qYa5D1ykBBmDozeSmAkY7f7y3HPy24UH7NXx4EQfzjsa1Uw+ZUk4nvXK7+h+Nq8KSrGeuz+TrKmi7bWpQqMKlBA3jxZiLpP2xcgAXpfi3UtEs2eCgu4Qj3GKu/UJ0CqDdb1CRETOv37l+BtzAPaPFrqhd+SNhBXL5wXNVs8ANWCz9DtmPWUOdLYSSu4uhLmdyCp9KDbDSkLiJ9WtAV4Bn1/PNOzDu34tyMuyOed8uQq5q4LsVKoztemn0HmxItbl8ewxtMyAmgko8qmRWYfErsFbN8ObLt8SuSsJ42f+eeo8d1eiupu+OfvK92gXSQcWLqhqLYgIrtwQXTMnKH2SptZjgeaLsxc1d0p0R6/isBjGFwHLimyCToJ6Bc4vllFgPoNOTWgovyRjwWZVqdL+KFvOK+j7wx0dGyR207tXon4/LFEQthbxfA6I8dQhVX8+h9d6TAbZFT2WHWkkrhgdpSu+43RvlJRhLMbMZJINxcONB2hPPz43RaoL05uhmXPRKsnMjefT/0ypTy+24JMzEQ8dXDFgO4JDMZXNOgFam93oxHEPjdFb5UU0q33VsERiCsXCq+d7StbuaKMulpozeyLdhIa9aFfttB67ixiK6TbnZ9UkOexmFwH9IYnsu+7lOfwgzttk1Z3j7wJuDwTk4lVvBYIS3K1GFt67pHgxnWwDboPsCFQz0M/xi3cFco9edlMJw3re3EYXf3lUf6NGdZ76aPikCnb3ek8g3jj2lLPbc1i3eK5etQ0gOr1gCW7Qc1igvirxNHNz3Lub+Vg2QCe/iz1gzRiqIRaQ8NfzOKXn6k+M9eD1B+VdSBY3nmtP8QinIXxagCJs2XLJTZFAVe/8TRaEGXmVnKSqk9yE+535GB1g7x2zKm3RXx8ZFt3UPc1AFLxUigesGOZ2OLzZvYyAebd82zak6hHIMijPkij8RZ43xQ8wLVqlH7yCYIxRFO4mUEEBys7df8rtGAp5aKY0d4T3XJlC6lIY91DwvPhb5DLLiH0YQQEAAQKlfn88G47ZG/qrr69GuRVHoWyPB07I8uHZ6dm6IeaTLcAnNLu+lbI3e58eI6bhcm8nQNpPKzsDzox9QiHgpwd+VQTtJrRffYhdyhn6fABpgad7f0apZo2MP912nMFpE0+M9vYcd62gHsWKFWYP0x60UvDsr1ba0TNYGkaAPatbVv0uV7JHxs7zViUqSxAWX3hoLAh4a+DmiyTb9fbjYjsN84vViB94O4JRldm8WnQPMcBzg7h/HzdB1JfWKsXzPDH8ZoAUTRvMgQde2e039Pq5z3xdgHVl46rMwziIpbSXB57Z6P7akUK22IEyIc4/mO8E6fZjHd3bl77YXbcyXRBQdbgEGsWOKiRKpwIpzSNjwG1poqgen5/a0re0AsEFJ7/TpUzh3+EctMgkklzJ2M6yyOfkXoITX7F94F4V/VbTca4wid20Wta1vkgCJikjgrfKIh+ZtTd7/jQLhrASj77SUky80h+ab0Sg+kpBIrTR9hxWcTv78E4S8blDLt6EzIVA93XTqZsHebfevu8als+iou9LQjlNgypoJernzx+WV0NQPhH9uUe4OFZBvMMH14KpG/XwrVvd03WePu79tg0qNPXms2QHmY7cQOlnDNAnC1FjjzYUu3Cy3cl9+9VA4D1rBA9hqT6enUtdh1GVP+0HNW37rfmNybB33Ox52viE+kOybwV5/uwm4JKdMGPWlZjn1UTsNVaAPmhe4IOPEjN0Y89qCmQrD274nLgyrPSM5KMw+cjeREm06kBpnKZ3NnBUDUhKn99lUmL1qZVSUigif0iS0wcNFn4nNDn9W4X5mfH/uiO8kqQpuzJiWXjZOYS9gnEkwdKdl3ZzfN7rBO3Tu6psvW/qTT4tPKsk/3hAJ6AQCMfH9Pku8nryH10QrFTXDQPl1nZpW338kCkRAwYGc9V3UKEmGBU3kuTocZd3ThSQPuWv9IU8gPvIMz3hSbZtxI1j7z+VQfET0DkTE7RjN9C5PClhAXi/oW0uWPZkgL6IeOlRNTraAHCwJu3IRL1QpSaVW1nCQswI72hgyKN3eEqm+ptCAKn5CLK0b9PpXGfPa4sUvQvnqKnYIrtOFmi8RJj6RnK4jhdwWA7lZj4L06lrH92xs53QfMJ7nKYoi1nxRFXcackcq/Fah5X1ZkC1iTnqjE5u5DHP3DS+qqJQblk8PcX5zRaE11puKSt9vw+b7JKloLEMBpgdnGG9IJUxbvl5XbdFvKT48tK7ef+8fXaONIJvhiEs9rJ7q8Xz4z7O3D9rfsDo6zxLED7roawM3rTA57aCppwkWrpcu20u6Dbah1NVx64mLbDA3e6h1jClClcks6HW9zX6xrVkfyTMCr0G2ylP4T1PiWd3KklA6qA6eT/l8TVDn9tkNPYjdC0C1bbUWJkkuhg9tQxi2o27uNDQGc4QZSjKfNVDbg8wT+R3o2LyK6Uh2SKbpKImMaWfAaW5VMZu2euFPhON09kwnFptam3bLJjZD57okQU880oYd3XL2jufjfAJ2akaLCusOvVVduo2mo6U1wN2PPS36+uc+PH483HhFwXD8qIBX4+p/f5xiAXyEB1Odu+6i/d7w8JVUWFSvuTFLWmjcfenkWWYOnAQJ7EIZbqNH7ZeuJc+dR0QJ1Oz5P4c7fCPgD8vMZVQtPoVd88XfAz9XdiVjtWuSuEH+fCygai7HBburtqsMHWdH8FVGtKASPKp18QTCPEKV9Tg+rwPpkEpMqOZk87O24M/6AZ75QWs5PKljCosmuOIHn48mAx1UdO36jsn9WA9CN/qfMucMi+nZdgD42oKnH4N76VMpU9is/YbImFoAukuWEiuFS/fcRkLqVMe91zPKGBt55kV/X2+aHm8aFTNkhfa1wyTFbYhm2Rnbekz2KRs3g2gjLYo8FfOQy7qmUq8wDw9hwzsuzI/3P09rih9xJ2bUPxY9ZDEicnNkYbYybc4GimUpnukB0EL61Ua/fxoyiewGBGJZS94NzVa3XTr7Gg+VAdPnGb0ZfNIiOB4Xwe48UkdZNW86tw4Gc2zk7tO1WllX2QVW8tVqr2BbkHYEDQNFqrb17devTd7qrzfrO/lLqccNjcMo9FNi/OujrYDGYw0dPh2oOjCaw2hKMi5PgjGnS2qnOppi1Bzbd0WGvx9Zslo3ObF2QI3cy4hXYYMe5oqi4030DG2xvF9226P/cdtNEdG9tVjmCRhRuQbJQOyHIB1FTiGMRKiCqKbiWJk85/7nZUmx7NZn30+08oYNo04L2aOu+Z6pnQnX7Y5qTPM2z9LHkQKVq+zn3xu6oV73OisPik9y0QU+YxvrxSIoYXvDLNDLjAZqqSNX04P5PDT3myzn7sQy2IynQABeH37dy89TtN1UhgUNToKFldx5c9iFHZNymnueRIprx6/Bn4I4UmfN4InUjTl9ROMDthzF2kbMFcWSV6IKPvHtr28XfWA0lvXjQxwuBr98J0uuSzkajw0ZlN1f0pT9CyNnzYdElObhOppERTMTIpXxLHBtRjMx1fJ6G3zlgxXwqj3OamHLJizePanFQCMO2S8/rzvwozDel3URi9/Cutz9e8ockXrmFqG13edUKFNr1jy+3iabzmF2QgNqm/igSGPaRq/2yxKQyD88DyiksMHYL9edDBlmEf/vC0ZTb+19xNwRxhE6hAV5AUrJbf7gFPSOIPAoW/agwhDdhyVKu4iwfThvOYShpa0Fc8gd6vD7Tuyog9YIKGH+jRr//laaY+co3XDklz71rsIJGIIyHf83A+HLjPxosLelBRHminOPNUnNT4SZjW9qtGXRxXg9IzGSj6j+mYTQZLN3/o4aiN+gXJ69OM00APo6vEYJkXyu1GyVsS/9e9+iuYlrURKEiZhEBiHJ8qcLsfjocCeau0yII1YGgoe1MvSQzlPmtK3sqLO0DeRifQmEVhFW5i2SYJX1MIcwZopZS6fdwuxValgWhLj2kVpBqhZfkUx/ks3PJ8dV9DgQClF7+PuP1TSe3TRVpC5JmIBi+9VeBNSarCqAIju07tGFESeNH6eYNsuHg+GHYHk73SmxEaZVqMJTi94e4BDciHQmBau24dyIs4gCb1XHGsSP9uJAYaS6xbiYrSCa85no8Ke0unHxj6Ext/SB157mUdrd0LPBNRjIsb1wyew8gsqNCzxVh9hvP6zF5CKPV+WRxU5CRcAiRjM+zFVDb07Xu9fFbUOaBBJRONIlIrQSLVmlS2OCFBnmVtrBDn06VEfBHHDzCIezSZr9XzVK/lLcxTQGrwsSMWzfwHcn9HiNJMTZyJ4d8+UwuCQ0ch+sASs2Fal5nzrfDKZqI1XJZlbFgV8Y4VjdI3ueVNDCSIdWm9qvuvECWvWWMx4h92kqHaCdbEPCxsipNlPbrb+iucbv8TxMM9Ajx1y43pE21rOnfdyFY440sUZL7E1NQ/I22uuGA0yfM205MG8orA4hJqKUJ0buPE4iHsSBCYY76SEyMN2wXSrSwk+KBkjqrCxGUd8n02KbIfpDTj1I38+MIkpPGrq1Ir6xMzbw+Trlv6SVfuyPYgZMgBynues52FqyG++fqRbWf25H5zGWBDTlLN4bMzTbgrIunPcV048B/LQzNXUZWyF5VHcYmHbNtte1mbZN5AvmDR6QLCdKAbFYN4OfMwfE8UckQ0EBEDHWqdNiGiKAE1HIoZU/4JW3KMyc993vXN1r0Jeyr2UmQBQT7dMDJ7wU1p1qAYIslEMKhGQs+cZa8UqVgqTeahXew2MnR4D6kE6TJc4jzzY+JMMmvSklPB6PPcdytkf7uDQQUgWONENwyTWK1f147gYi/GdERtsyJz4Cq6nNsSdMTm2VxVPjbQniOMrKLr8wnIH2Iau6Hnfo+5YNDFKg7wvgZiw49t6efmWRWnWLt676m41KywfRncFe7Btsfa3maOrzY0Ll6B7qFwxi0ihbklppTm/FebbGUGf7WAZWWCoV6mtQJ1A3JLLuesXZ3oRFeaNkHjh/Usrdmi/LBd8WgvhWz9HR4q+7TqU7YrhKAHEzCrmk6fms3G5IAVaZBT2hDY93p/MxEpb1Jos+hZwzrCit+R68NYlrywQrJd4XDScZRUvB7BcCKokRrcXeSTPHtaRyd3EKIquSXJc25Z2OpWvTDFZoxaBRR4JbbEnRN8efdXS4atGwkcAGTuv657JWjaJBBf10XCd5t4QWE89C+niIEhuVSw7+5nQ13GsvOteX4FO3FKzUGjyRmVw7pdG81nbleXBLgX5AOxGyJS3sMGWoWBIIX4q3n1+kvr5oeui+dT0+/Mq1XM1ghqHNQSmLR4f76AVcGcvcKImyi2Zwm1Y3CXTtSDGKEm25FAg8rzJKH0irwOBb5xgCqkNEcsbOc+yUAY8op+Iw6/PmBm4mLbX3Ys0JfI4xWAYjygYfg7tyq7WF8SPrpqq+Q3h5kj8eAwku8axhRBvN+4uq6anzfCVnt0+tjJGtTHOls1CDN/QkXd7W8VtYW+obBBUJrd8CUPrtvE/ye0ndAv+/QlyQLYQz45FT9ueIrVqjXdFUDC0tgXb3GhavyAFQbYUOiHnszxcKg2MiN72vJW4weNmP+Y5gBXCPYy3LnTYtwR5QAolP0mFjIHxyTOMpXKFHOILzN8ahXTtT+jPBZwTxZ8Fu9XlQSrJdYzllkN1smXZRVsxutkMaM6wNc77y6ped4sr4Ho3A+QZEtYHS/OgCt/2v1bQZASWy0mei8daOA32xBe+Pdj0ZQr+Z1jTjONkq+ME9ZlOvXoug9p5pwQ9b8/IJuxUzp4MdvHfDc89M8Za9v3BX9VfMF1bYKYightZbMo9kuyuNun3+apM8lpfrgS0+xsKRvf+lK48MMRDOBMjrFPZkV7oK6sS+YjnVBsonotcmYAlWfGdd+NQRVHdcGj3q3oFGYOjrS7J3GftElAjffGQP8PFJMk8tjj9LY0P5j4GaiR7zgftNvx8hdr2iL3JCGYKdR5xXZm2ExdNjqScJQZaW4zrOjdV58Ww4L/JQb8YJ61YBO5QaDjHajIBPnq1bY1Hg5djjeSTuf3gKwWS2GamfOhCrPqC9dktFlkkiDNL1VRqW8Z5BpDQ8yV529Pno79a5AaZ+G0duimDXJob8Js8wODzv5MHvR68MhsPFptjH8iyBubB8p+AvdxiJ2ISx5eGxnshcLr6U8MhuRllugWmm25JEU4wChy36uFiOpgv4JgYv95+8Tf7PDLLVzBLZcWrF6o5aZfXkOe1VM+jr4QfracIK7bAoOtrXs2be5u64U1hyP1mARy5PYaObL33lIiNCXxd+8joLaB8h8hP8YZdyX73dkbk0FGDVjuoyOiwN9Zhx5Ko7BxNV0Hp7bb0FuXGohU+fLSNuzG2Oe5DpM8bc7LiQd4IAnf7C3zZA1o84hcQmLCauT4zP4HIPslw5C3daqAU03xq3Rr9DCfPihN3bBEMwWPakp4wS5EGvu/WjPPllRSfR1TF2PJ5MnDAZTTxW639EA6GDZkJnBOf+4LZkKh4Fh0Sq/RyByPZK4JNmQTJP6NcE1RpzhzTUxljXh2RSNXNvfKst0bnrXZvdcyU2bodQl8fTJn3U1xMacJ8ESwFjHTMPF+1PJnT21ZepvOlX/XGJpqDPngkvxZN82M3JZaXf6TR89ldAUjBvjqK3DKryPGELwc8adk1QbJpigeVhdpqLGtf/8RInmH7znPvjGHo5Lvifc3cESH7anlxD0htgTCb07dsB1yqW4EtBrKqTsJjKKdJqO6D2MVDeSe0CavvDw060yITT5qmQVLV1+6MQ+0JmOqTsxSCGLpeYoeYmCCGsbRPe49ai+ss8MVArEKPpF6nlFVb/NpEykuMaW55IcUuZT1LrkF0M507LpMXBRdwAmKYiAvWdpGYrwWifEj2koQ0asP3X48KKMfAtEm8R8wOdabIXRakp3DFhAn3Y01+NfiFwfSJdOjMiM4dwC4IO8VJTGoaSEYk1vqlk23f733+OD5Mvrv4NX5I/cCV8yE71ZlflnNpVvmU+2+FCmZEQ6YU7exLjoOruJ+7pECf40D6OYVnsG7HrFHQ0dgaDkL4ne54P+48fSNgzrhOSKVvJyJfPA/hHoi6pKwkjEKAamHUdtEmqupE4S2wPupV/uh5TDe0izzuxnkqBCq9uuHobFLQEf/78i1IhPPNXtsFbeArU6MrhJ2Qo9iLGHQxtDw4mQd8MupylZi5z6Jy5MN0Sy4YWWGPZwwPmhy3udhPZgacpdTvNcvynzrS56fsz96WYQYRiNyUgGYJk3WMUWnevilWzyjQ9Q/glIkI3EUNfPPs/B+siWAGO3OKycmOSWUgPHYzCZQzC/MdAWEalwUcUob+LjS6uR7KpZbnMqY+v9s4SQzxMzZ6Kym60iyNR/U3ywzcKedDJHe2u0POMbZ6MfoCYml1DC2lkNaZ22q0H4CmeksAV/v77efmcAMyb3AF6yvcLvdZlwhNiuKITqam4CQWviSWyKXhrk3EptcNyTkgD2DO+xNzMjuHzdI2yOOVkSBaKWd2KNclC9XJXfgqAQtXMAi8iGXd/OuVgOIHXx8Bvq9rwOC3zVUSVRud85Dc5VQa8glZVtBEvZxL01h1p4fgeOhM07dkQttVYmAoq6p39TOJ8CHwlr5hovg45XCOMrP7ZwVgNJaHzmyQ77RYfax68839wR7d72upG8p741VEl5uefiR8ohGb/F0AduRSkzjpRtNhZpv0N2F1vqOuCYB/ao4SRQXp+lzRK23CqQ4o1FYjt8Xl20OBK+7hSqDTbVIuDuJveaE5W2Jrb31/u5zEdVtJpqXhVWHVXbyC0bcmOye64AZM4JYnKdfk573AZG1KpUXADEgzDgCmTRy6e+gT1YAlLWk06PP5fCTf+aYEpy8gdBYiKwajLgnm3ADRQNsE5lnz+7YS/f4uDIIxx/IYBnnlEUMZGRD5C6LfFqzXrxkzWHp5jNSr3+DdoeGyDQc5SOI2NkjXDpdGqhiPInd5oZXLjmjvh+SME9PcBOTkBdEfCEEFDjinI8d+Z4m8swuE0tV7XcDcqhCPqCLTzVBRac5R0hrtDA5VbQqH7ir301V6lA8xQ3dv15uiZ/6OGHD6Uwxe/hg3m74LXfzxuMqCEZyT+VOsZnXRRpXci57M160TQ+etDJh6PDjfJ10NlrG09iQP2qiODPb02n+95/RBM5DFkjeyqg58d8D+GQP+Wz96R98QRL+fM50tWKhU8cOuW98VkgDERMjUh6XpioI+5zEYLiJ72DNbsYqou5cBhOf7zGPAJwWbSTwcM53Qn16uajHySfQd+7xD/Ujf9n6VkEQP9k2UDLjAfNdaAm0Qqn1xtJvMhc0rKLU7JB4nb6yU8KTnoRn7SSplv9Nc1XdeStj+4oDuoAzvrosvyW9F0LAPrBvljyqsjnTXaxncvxZYFeRKXK4Nw39nYGydyaveJNOaiRs/XlEfwaZ9NHOtDeb4fOAYwNcOL/Q1+bAcUzz1eqCkOQ2z7cXBN92/byz67MSLn6URvCbhsjD4RnSrI6QDlfLFVcpUOpPjjGVldb66+tiGyIA9Z7HQmvLgqvCdMex1QPfdjoaBSo/In6EB39qWEYMX76NtxkVBM8awZsCVzjSpk2EZVpCNX4b4lLuNul3PCzVXf4L13Rz0hRlBwiZR074RmvuY3yaDD/cofrWTVvvOaKusDVdBVuhrVIqIXG2HaF9RaY6HidbvSvS55DFmbQRXesO3UAtOgHhjJ48lt7wHwRl1/iY6KlJUoFqLoIuw7T0cIsZUoTAuGdehr+F48pcOsKaCWAHXXN/27vbUMYVDepif7U6Sk6BqXtRN+7HkHHCkVkllJG4TJPwbj09iPMeaKCrYDXnWDxZZZaeH1FUJMgupX1e6AwKizC3BKNw0zVptbI0HR599L2W3+tW+nQKuGfzNZktPRXxePOQBcWPDukRjT8z7IwCk2QZJK+HMivInpRtQdfSgOsPhm24it9uf95j33Ge+S2QgUNRqvloFKraxKvoQlS79ZWmhdANSaXYF0jzrsZOXVmNLovkcbTK7cU6ST4NsywL/3IXQgj8eIQyCHLKg0W+ThyseHvDXd0Tj/sm0QirhWssNJGuNDG+r0oKk97wr0its9c1+YggizRoDCJnuRCTgNfHoS0obea8XaEKbtpZltG6EkqWX0HM2b3b+0HMb8XHXMHq+b+7zh+GbRn6wg1zMWKvWT1c+Zbjicwb88/F4nI9kThPRcysuGpS0qSzXklvh5iBLItjv8IlmcYe69STxjiFOvuOkQxvPTvQMGIfDxyIT0j40b/pne7ijsGlIlRrehILgQ+MRxJWDMLy+Cfy8AerzSS+dN/yp9R7PZoTvA8qOcClonAE52KBx7lJFfU2Sc75MiNXD5KGGmyvkY/fJPpLcpgOiilUanfeL9qbuPuoHAL1TJYhyuNZPOm7RJ4jg24QI1aff+kEt25I95OOKTk2KdnHixwY7GN8XcKUEyWdsKBw9nyPpXd9dTt0kvwsuRakgImC9n93WZjh9BeV9y5+YnI7WaDHGzYCrIkp1KescoFlb3tcMFlWhS9/Thm2huwdJen2+P8wjtKLbOBvQ29wf9JuWf/fTiL/9qNNf/yAX+o+/4UT9Ab9VTFEolWJJWqApitwI5s+/I/2/40ed/mc/3aRb359i+vnpJlbdn9b53/npJjyAd0M/IqcqK/EPP930sZApfFUueJJ4sYotiF4OZGQf5IjIvNDjYlKzVQWVmDjevmuKl4jv0es92zlsGVnz/1PZdfXGzizHv8QcHplzznwjuczLvIy/3hzdz4CNaxuwcAQcSCtKO+ypruqpbuaCiKFH9Ias+6pXP/aFhYCH9sHVJIGg7bnqXGsDxNVOm6lEsa6bwlWX+K+Vm0z+4arJjp0FkVN4B9Zjr6qdhOdxyimyoEjDivcKeT/jz9y2ypZhjF/7GvMqYTZJPmabWRDGfWttZYyBcQuWQa7sZZxpjXF2LaR2eRaYSHdvZqgc6SfwpZHLooIyP4qjTw1D3CSPFvFTMI8jGELSGUIori8rCt+vN7zMwLdoQE5DMfZNGjPPvtgHtx0vZdLhh8GTzOhJm7VuMWMaXXENExSyxKf01OGY2T9QGwbZpQ/p0+wUKicYvc5KQxvQkA1kNrt29JfbkXofAfk3wWj0NQJHCDYu6WY95fRHOJfsCmmb1txA5S56siPTbAqKfE73jhsRJuG1/ix89N1Yx7TOZHIfOXNQJqJaFo0oDxXLUfluZHSmfF8xdCIgqS+KVgmYfnLhORtwThEh0ZAx/M7oaxo0rVYP1vK8MutkHzppeqOJJA0Ow6Ht8zrIuFhPyoZja13nNiagTpFfX+7BOqyDy/xuJNzP3h0H8vgpWybWS4JXYIPEuOKrh2gvf56Yz1UKWrmw1COjEmSwBfd8wVEc+4VDDPSw/BZP0hazLricfX87W7Dsr/WkbDEkiSk2/WR9k+OtZk+zvO0EcKisfevx/MW1TCCqkrQT67pMoeJeyCHOgE4+Fy3Mzqrgfawxk0aZaDSO+CtYbNkpm/buc/MQefLqJKx5OqmTE1FzLuB9GlugxdiCG+ObnO0+xtHfFr3xg9boMLU9TfFGHwWK/pxwcQF+/E5MLJUK9suMcXeWw125MOtWQZCTIxkeE9feOdPx4n/8imV3Nl/7BSxvzd9b97DhzaD+DNi4QEDgRCF6Ly2SGzt79sNi062Q99b/umnCkY2U/1yJWseLyhAmNSLwJNMVznpuUjTmwc629WwoH4a32AMHtHupcpBvGynhuVNM1WTlRFmZT41maHyccAXERo9bvi3ZFtND9zfXn2WNV+72Op1S1oDYXR8NMNYuUGLXgiYYO1HSKPVUDW5AdizMcBZr2u7PyOqG0GFSv2FkloCNFbyMcj8i+n2pZPvIc8SgyhPxwpFeHqAEEVGv9ds1vzEGRY1IMui5SpxJIvvpH0Xbvuk5P1L8/krUgujp14lqO+ZPYOlyhng5OdgtyqS7ZIeQ4UaBMAuPNucDemJZLSP3idof2S8Y0Ruva2NKTz+MiVogMghWz2ZxLKohrxkyri8YNLV+IKf6/CncjqQsMFddzxkrLEyQE7CyOLDA19KaeFwyxbaL9vXPJs0pam6etbQYHFQ0f36VoqacMHUJy3SUCS63kjSksBpu/Xu4aVj7IHzYBEPszLoRBiCtmrXjz3bjC75fjVLp605Ikka2XD+aCaaW7tXyIL2/62IjjYwZEGTfEDYXNGnN3YzPWsKU1zqHdYISt3O47fpcRqx6OiL9lI3g/PqcgXL9hBKQkBYtsI5tOWf7vfppDRmQdX2Yc8lUHR/4xLC+uszfvjkspezupqdJQFVBSAUUib3cRzDkS98Q0vgVTDzzwHPIdtn3yJlhm4YvjpLQY8wPVduXfSAFCPVrmlPSYgL0BrVEhACxjuGG5iU/9pUQEENLf4Mo/C48ZN/Svr+DWiUcn0icdw855Ubmifv9T2SP0yZATWqpaa2Szk+2q22gdY2qVZMsp0/PLQ0JR4ylEQpVkqOgnp5NbZt0bK28NnjVwuWpa/EMqfm571TjxS0yyX1UuRH0OGYqb99r7UNVmXCjiipDqRhd2UdeuzmOj3KidkUz2goS75wNUjbTNBXyN856+IVFu8ehCCqpioitHafH4QEjqwpbsSk5MymQJdw9f5X5Gzey6HNfc/GqM+l6z8KDFytuG+xxTFL+KYiNEfznlQyD83mzIt+lXkeBc0bBtXEPaFa4ud2gZIvZQE7eFoAwMVzu+/uWAuFHVi7TfDPa5Qad/scf0psqsjv/XSHl9PFO4L+6ak3c7YUcD7fMKonuRatkfreRjRSQJFEPQXqjdUHbfWNlY/yAUcBdUnJdiQgbl213c8JPTrDpBo072B3sAQNfSZ3YMBXbWSUA1fO/xs1TUs042HJiaZ4MZZt23206g62s86ijoDFZlNjSbguMCaz12qFLXXCIH8VX03mSuzmh6jzx9yMffBRMSB+l91VeJ7kNrawKEUHoRxvj4+oOSKFbQTULA0/FmiEaH/IhqUc09pEowjbmnW9LQr+G2aSeK0orS7fJiJ/REoxMRnmjsgwLLRBiSL6ac6yh8cn2qdXT0rOW9ONE+fTELzlJrGS2rGE5R9qcUz24ilfm/EAlhFxJKqSNFYYvdTRNi7Fw26VlS4dKLvK4HGiD07YheRPxBZ5CfaqvwC9nM9OuIU9Oj5p4JsuV4LIKJX8+nVX0j7xGxAJbJlUVSUHv83RQ0daAyYSir1K2xcaubcUeEtLgZFTbMQitg/FehNSELVQhYvWJLa8WbeeTqVOeNfnc30wRCSytCEjuyWxLiJksIqoBb1sPymXK6hzbnarCQRcjOZxhsV12e6ABib1Jaal1KOHNsdkrBZ53NPJM65NCYQjw5Jfy42+YeBT9pM/GN+blYodVg3r7K+7h4+QRHh8BrW2cg0nB1l5D/aEYPYIpLm/XlopQheNhfE+SpYGxZ1op0WHIZ0mU1CzYko8xuU+gaS2/accfBGtJOUL5Ip3eNp3wT+k0Y3snLGqDI0s43xtQLf7VmlbU3kUHjaefSZzFaKwQPRVTcslkmFhJjzRFX+TFfFa04GlysAc5ygrRjpS+pvxlZWaocrMssj8mDYjy5Y6XZBQNunx5mr17kN+/Ay+d8K45orHZoz2oM0ve7Ch2ZPHkP/bjfEmbsuKL5oVODfrBlycMFigqSsStBgX8HdTcY6KjyIceEfXow+TYqU7O7QF4msVtqy7e9m4erCetiYk/5N/fGikelKgn7mHdIdE0VgePzK3PrT4pE/3yq0izMLFJsqibsYsoGPvA6gyFCZd8eUfd/szy5XWHwK1xf1J7Zj+xSHOIouTnqi2YLFeKR7nnPFC8HYju3wvH5fYwfPCUkdUTJ32JdfnEv9EO7RMfc1rfvrfHB8zfjJ8CxVPsDP3vz9hNWdlZzUMYJSL9D9EDnbCVo3debLv0vRGXFkU/FRdL/Ip6yUKHhCH9fJzDPjQ9e6hcoMMvQyTBuxMbgz0r34UuG6ghgsJZLhA6dZzcZbtj0K/6ukA8xyJrFGKOH2enMaVl/DnGrgSXXGg3G2WTp6QhwkU7GyZWGHwuMpx+NxYoxM3CRtKCNmxpyd9c9aJK/GRzELNOk4/TBxs12jk1lyj/Dhvez7bq1TaGdKe8VLIPLnx9pbkvsi9ei3vv/LVZD5aYzyKWqcb2KXF9lIOkh9lxWMJYW0Ct5vuC9ML4neFpJIaoHdXiXpzUuFRqJ+HF+dSgbiyDV/pez15afeEGEhW+TFx8WKSLH1Swqh6CDT/AlPCBFSshX6lkZ0bpjPieC1TtxIPJ/6Cp0lgdO0J0p8auX3IrpeR9nxMz8ceUWIMyMOO/TrOhhS11PZZUndc1n/RuSI0JQsa/eeDnn061YjyDoQhrA5ZnoAK5lx1p8TZPI4Ujg7U944N4f3FFWtQTL/56J1R7muycccfLqMw/AvdeSCK4OL6/bduMgog+e4eqMfeAFNfGMBRXinGByoHoDaojm/HSpWKsfVHXULLy5t3M25yOVu0aZFMHjOcRiUGGymZzpBQbQ+1I6MUGIRwI6J8p18rmM+v3fXlJhybNco1hOIxbIainLJj/uXVqI8cBWZUEIoTlZTJq9b6jqprFQ1e1uYQU65GlVI+OVUiH+zpkoilq7t38VnmYrTEAdMAVblcRdScXANTr19MjkeoPBAIJnb3XnmO+9S2E2zb9HXHWXnGlE/BoNGqV/py/gZ7V92c6+v5GSI62SA2KiYe81Q3SqNi1WIBgB9D3pW+S19kqkkO6YDgwtvQNZuscQOhP6jP9+Qu/xvuxtSP05n8tj0YPuaneBnxxPVMm0Qm3b5bPNROL7fRVHWnemXxntC/H/kOkTcZEk5IiB6ybGiR8XXDpk6GymBgOVfgFn3n4je/+zw2SW6cOc4l5E9C7af0+8KUNF4vVY0b8glxAAKc6Lyw3sfGbeLTD/T2id8cqr+TGxvzrVmMO7JO892siX3ROU6PvVy2w+0vDXSfl8ek6RSo4XDpSRC4CJjjpw7tVNIr4xirEyZO9W1j1LbWDPKYbY+T4vidGrZdXlAg5Bjbb58GZEfnr9NDZuoM5yarErIFgJpQF4VqaD92m5xyZoeXQ4cXvX6oh1cnAX96iwpvkXOJ5fNT4F4x5tWWLpxEgppM54Wpw1Nmqo7Bsy8l4j/CxZ6hKYI1Pt4aenCPls0ey2MB3zfzq5RYTbGC5YV3DVOOa75G1SBzdiWDPXBKm6JqI68VUKHolED594+E+8L2IR4+kZ8SAmcFSTYm1wb7/JqsptI5NlvQZo4vfPkzU5gCIRKivN4ZXUQlVmBLeuHp5NSrPk8mvjs+WT+RPxw6G8kal/RFYj2G/vCROMPsqMiWUFBOfrCRqzRfLMamq3eArIQ0QOqbi3D/Bp8Rq6InGEGtvjdmEud7/OkIWR0qX8szgRpsOWSH7mbrQX2OlF1De/+68jsIoFhDVV4UgHo+I3IyFL+LcOhlaBeynt3sXfO8JMK5+TBXXTTHC3LaVUeakJFr57o+ZiZM/uTIWI4xGsVWdn32UCUftSYwBmkFZHD4qtHnJAHqVzHGr+NdSa0ZmrxvgxRuKC5YTpAbDXW2RxJG66KmPfwSn5SEoKqr2uqcXG00J5wNjthzBVMZIpVr7RiRlG/VXNSIJikDJbbdoJ6Ta31RZUdSX33W+W1KHXkkeuH9TidjPL1O59TRDJJ5xRuCj/kJ4OzLUaRi/xF9HoEakxHm+lOAyBABaJVk62Zm0r3zjCiVeHaf4yz/T/uVdcF5zUp26CNGufHumQtZa3MNeM87zJ9ogKtnR4xFk4YWK2xsyobz98HV2u9971ptQ9UtTpapsVSaW0YpfdQp7glUh93rN/bJ913zNwOFe/dyV2uqhvAafEuzUnQCAJTlOjSnlTwGnX5g53RZJfVNORWAXGvKc0m3/x0pThn3/0K0Zq5vO95E8ni8sx4nlhPwsfZ8+9/EiFjw8CoohIz2XvagRcb/Ngf4EwlH8Ikinzj49IdGFr2NSBJdYqAk4HJy/hMQxhPzVF/sBzjh5Jd4+bqSYPaVtkDPnsnRxvMNR/QBM33KxiWgEnpNA1cPLdSjkmxG60vK2WLv1cX2Upjgw98Uw/BfckpSJLP6bXDcVf3wyCgoW+aaghd6RGB+WX6TcNPGqZ7H8t7hSUJfokc0u+mgjJ+zd+u4dYHcR+K/AyRiJEaBgM7bD0hHNk7NwU6PT4PBgzilQzs+y2PxsfN0rdldETX1dSJDpRBgyCdHyAHQ6RPXbjt2kYb/Eu9cTIL/Pu+1r/aeb6hkldooYBbhgb+psQ3TJ7tInb2mVgVXTV/2yrj6Hl36YTI/HGPTJYeCaRNgKc5xPjTZs3cS2fe3uhP++auejn88/NmENUV7VBvQP6tIa1/Um8JpN8ZzsRrG+gizJS6tnqapbpD7PiGjiGHn5Ge0uX7pL0rn2kUTUpxY2sDa3uopz9CZ5/rOFU7n74AiwiosyHBhvRNnQeL2sE0c5sskL+Znm5EQNnjx+YDyfGNsu12ks0Sji6B9eF33KUXoS4o4LtYARaDMFSEbXmnTiDFd3Rdxrfuu+JoexaPNj6rZ3N+ROfH2v1Xq4nsBK+lET2EqsQBL/KCS2F65Gf0LHz79MRVKfvuXNzv4rBfHHlxoH+u9ugo1NHCRiVKJHrRtiLZRaXIZMoZd0sH+9jM8h7S2uN79OSMLl0xdaIa6e3+ioAjt/tqDU5qWf0zGBSQ0Kz8qocFJDoo4HXIN3a26jJ/KM+eyStTkPGsWWhuVjyTIFlvJgd3F/06088xN73BNnWU30d5LUGSbwNM7kTXmg1DexXCWnUMUYROAVV5vr2Q3P5O3RF2z+t0uRMWsPrveUtX1Nt3iBdUtlEij8ceTtOhQueAcChWaZV/StkM4JioWOAqlzl7g0ivTysPfrSbjSWJrZ0uZxqww6Lh3W3gGQ2JgrrI4r4saHI+G4HvGyQ8dPIQagJUNkobP7Jix0wbqURYMWa8nFI/ReFyX2KSi09PZENT4/PhrX5SGvz/0kW1/gVipL4qJfx6+yUlEnshnN4YIGSAUMqzkC2te9y7fQ0mF+2OEAjvUkX3J8A5bUeoNDZ3+jIkKJ8vJvMtOeOtBQjSO3dReozhFvODxHZbRM1l7bS+JogcQl6jG+S4HMfAUhvsqPVgboqzYIaqWWuGFrX9HOB1mwXlqZobiUwSo4qb0fOVrUF2Zd6Nt8ed0A0CRCmDXHV9oeRJRSjFPkourXl9tWPp7Ec8VeYVP2IdWmVo22MkIbF7aqKqsg9+azbbsG9q67n1W21Un6VU834eLtrr8C0ZTuXp2ULh2UpKNpWW1KR3Mraid7ty6tx3gy5fy75XHwxxmgegdcbvLIO3IBrAWoaKMwZw/0WqtCWH6w+ezT7rBsrrtIRCJwxDASL+1n3oRUMAeOnW+03fyakBYAwuej9C8rfbVFLvIHYnb9fUm6DCxeoXIRJcmZ9Yzb74qVyUrk8yaSMqJ+SsfsBuObjIrJHX1O7vMj9vvZnqpz2YhBe5cF3QcjyiZ/DDTvqk4oulDx8EKqvBmaL7vPZwypvJoX4Mc2HmnYLIBkiaekmFm6h3TLFqBfYRts3cHQo8N9GBEmTWNl2MaP/cmminB4GeInunoBsTZQ22kB9/khVwf81tt1iV0Xq1vRf6eECdtswjkd8GtOSZxqQWmwnhmW1xdKo5f3dBjiBtTLP1cMKBf2kt3iEa/Q57BHQl7aeIHF3wnICa9vfeqrrWWl+PdoAFlIq7HiqqwoafhC68HagktVOFmmkRhziy9Mx1JWEXJXX9i3ish+pcyNWShDODeT+2DHtJ2QB18PUvGUvXPF2hz1nM/Vors69AO1iWamTJeXRcuX5TooqiduYsZWNuVDRZY1VfO6qyci13Kpkb+avZWm6z95I/HQh4Y6OZoAnk+P52bYrxass4Q+f/LKkobUJBSu5+f7BwNQ+1dzFXjglGBy7rMBU4dPFlsx0dorH0f31KN4RaDbQDoNYamkfgPUC3hRiDerKRPKdoXCw03FZ4CQQjD8QWT9p1ZrZDY0+QLGXyQLqaoMUsWEfcMxePnMM7Xz2JksYEkVUVZniaJAgUpzMx++kknZiPAu8R0iLcbHfGT8KfafAeyvC1NV1QThJaqtOv8JNaxYmeNnTQLFoHLEvXvtdrDBQ5X6w+js7WSgL0DI5YEYH6gO8JcNWKTV5JspE+MgGFiu71XWtKheKly+HF0jwsgBddiLpYD/zcH5N/sfvjmrdW04o98b4W45RtdCuO8xpSLMUwa/5tZocfs61Dzv5pvO7ZQlpPS9J9hpIFKNBA00+OonW0oKdt8fkFN3sI5exV3rBhyCIhG7cI7QLx/ib4rKdQq9GcTT5OTGK85NWKWUYp4/VHmi2qMV+qkrhx9+38vgBZgaxG5l8IbcWYICdgSBnhpWGFSA+u3NH8CaZLzfeF6ti+yWYfRrcr1BJuS+FN6mFPxNjGicKgoWjZuZl579DpJodUHCGGAk7bbAmUFPlVmlTlzqmGIBQtTX1YZ9rXv6NYvaTZNGGUOmBCvGlVLTyXLOKqWYfROmBw3loFDNbBf42xAEszg4KhqGVomdMcMQaKUnbPS+6kaLbzlc/XpOJxpXqvQjbeAe6xbdVb84VS8xKhNrUHBBqWVWe4iC1lgUssuNlxk9YFQeGy2+I3lE5JdOiSh7riLaB+k/Ar11rQtQx6eYV1vx1g6YUesOasTflOejzg/jP5imqy38LhATm+8aBxZChU1UmkmHEvELv8f86ZB+4/L8L5m9S1J0JXIUrcqNSWr5p6EMqwsyIHffH1NhD/lyF7hTLqEeL0JDpC2o3kX1SKQkFuw6hD8rgy2z4ph9yo3u5HbSx3+eNZYqxyvMUHxZAGZ9U4l88/vA4B6UGSiB2vh3y/LJXh2kfiRpcSDDZHnm/gV7hXNiCc3W+pkhIfmDfIGA26RUv/sCykABQllAMzFY6QFg7VfC15H/1drybz6W/8Ht8n88KpD6b9YWFML//VGBEPbv5pb/9MD8P8wtgEWABzz+l6dKrtncGNOnBK/4Dw== \ No newline at end of file diff --git a/vendor/github.com/systemboot/systemboot/uinit/main.go b/vendor/github.com/systemboot/systemboot/uinit/main.go index 596b901..42895f3 100644 --- a/vendor/github.com/systemboot/systemboot/uinit/main.go +++ b/vendor/github.com/systemboot/systemboot/uinit/main.go @@ -3,33 +3,49 @@ package main import ( "flag" "log" + "os" "os/exec" + "os/signal" "time" "github.com/systemboot/systemboot/pkg/booter" ) var ( - doQuiet = flag.Bool("q", false, "Disable verbose output") - interval = flag.Int("I", 1, "Interval in seconds before looping to the next boot command") - noDefaultBoot = flag.Bool("nodefault", false, "Do not attempt default boot entries if regular ones fail") + allowInteractive = flag.Bool("i", true, "Allow user to interrupt boot process and run commands") + doQuiet = flag.Bool("q", false, "Disable verbose output") + interval = flag.Int("I", 1, "Interval in seconds before looping to the next boot command") + noDefaultBoot = flag.Bool("nodefault", false, "Do not attempt default boot entries if regular ones fail") ) var defaultBootsequence = [][]string{ []string{"netboot", "-userclass", "linuxboot"}, - []string{"localboot"}, + []string{"localboot", "-grub"}, } func main() { flag.Parse() - log.Printf("*************************************************************************") - log.Print("Starting boot sequence, press CTRL-C within 5 seconds to drop into a shell") - log.Printf("*************************************************************************") - time.Sleep(5 * time.Second) + log.Print(` + ____ _ _ _ + / ___| _ _ ___| |_ ___ _ __ ___ | |__ ___ ___ | |_ + \___ \| | | / __| __/ _ \ '_ ` + "`" + ` _ \| '_ \ / _ \ / _ \| __| + ___) | |_| \__ \ || __/ | | | | | |_) | (_) | (_) | |_ + |____/ \__, |___/\__\___|_| |_| |_|_.__/ \___/ \___/ \__| + |___/ +`) sleepInterval := time.Duration(*interval) * time.Second + if *allowInteractive { + log.Printf("**************************************************************************") + log.Print("Starting boot sequence, press CTRL-C within 5 seconds to drop into a shell") + log.Printf("**************************************************************************") + time.Sleep(5 * time.Second) + } else { + signal.Ignore() + } + // Get and show boot entries bootEntries := booter.GetBootEntries() log.Printf("BOOT ENTRIES:") @@ -59,6 +75,8 @@ func main() { } log.Printf("Running boot command: %v", bootcmd) cmd := exec.Command(bootcmd[0], bootcmd[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Printf("Error executing %v: %v", cmd, err) } diff --git a/vendor/gopkg.in/yaml.v2/decode.go b/vendor/gopkg.in/yaml.v2/decode.go index e4e56e2..129bc2a 100644 --- a/vendor/gopkg.in/yaml.v2/decode.go +++ b/vendor/gopkg.in/yaml.v2/decode.go @@ -229,6 +229,10 @@ type decoder struct { mapType reflect.Type terrors []string strict bool + + decodeCount int + aliasCount int + aliasDepth int } var ( @@ -314,7 +318,43 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm return out, false, false } +const ( + // 400,000 decode operations is ~500kb of dense object declarations, or + // ~5kb of dense object declarations with 10000% alias expansion + alias_ratio_range_low = 400000 + + // 4,000,000 decode operations is ~5MB of dense object declarations, or + // ~4.5MB of dense object declarations with 10% alias expansion + alias_ratio_range_high = 4000000 + + // alias_ratio_range is the range over which we scale allowed alias ratios + alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) +) + +func allowedAliasRatio(decodeCount int) float64 { + switch { + case decodeCount <= alias_ratio_range_low: + // allow 99% to come from alias expansion for small-to-medium documents + return 0.99 + case decodeCount >= alias_ratio_range_high: + // allow 10% to come from alias expansion for very large documents + return 0.10 + default: + // scale smoothly from 99% down to 10% over the range. + // this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. + // 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). + return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) + } +} + func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { + d.decodeCount++ + if d.aliasDepth > 0 { + d.aliasCount++ + } + if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { + failf("document contains excessive aliasing") + } switch n.kind { case documentNode: return d.document(n, out) @@ -353,7 +393,9 @@ func (d *decoder) alias(n *node, out reflect.Value) (good bool) { failf("anchor '%s' value contains itself", n.value) } d.aliases[n] = true + d.aliasDepth++ good = d.unmarshal(n.alias, out) + d.aliasDepth-- delete(d.aliases, n) return good } @@ -746,8 +788,7 @@ func (d *decoder) merge(n *node, out reflect.Value) { case mappingNode: d.unmarshal(n, out) case aliasNode: - an, ok := d.doc.anchors[n.value] - if ok && an.kind != mappingNode { + if n.alias != nil && n.alias.kind != mappingNode { failWantMap() } d.unmarshal(n, out) @@ -756,8 +797,7 @@ func (d *decoder) merge(n *node, out reflect.Value) { for i := len(n.children) - 1; i >= 0; i-- { ni := n.children[i] if ni.kind == aliasNode { - an, ok := d.doc.anchors[ni.value] - if ok && an.kind != mappingNode { + if ni.alias != nil && ni.alias.kind != mappingNode { failWantMap() } } else if ni.kind != mappingNode { diff --git a/vendor/gopkg.in/yaml.v2/decode_test.go b/vendor/gopkg.in/yaml.v2/decode_test.go index 9269f12..f3af685 100644 --- a/vendor/gopkg.in/yaml.v2/decode_test.go +++ b/vendor/gopkg.in/yaml.v2/decode_test.go @@ -691,13 +691,13 @@ var unmarshalTests = []struct { M{"ñoño": "very yes 🟔"}, }, - // YAML Float regex shouldn't match this + // This *is* in fact a float number, per the spec. #171 was a mistake. { "a: 123456e1\n", - M{"a": "123456e1"}, + M{"a": 123456e1}, }, { "a: 123456E1\n", - M{"a": "123456E1"}, + M{"a": 123456E1}, }, // yaml-test-suite 3GZX: Spec Example 7.1. Alias Nodes { @@ -714,6 +714,14 @@ var unmarshalTests = []struct { "---\nhello\n...\n}not yaml", "hello", }, + { + "a: 5\n", + &struct{ A jsonNumberT }{"5"}, + }, + { + "a: 5.5\n", + &struct{ A jsonNumberT }{"5.5"}, + }, } type M map[interface{}]interface{} @@ -840,12 +848,25 @@ var unmarshalErrorTests = []struct { {"a:\n- b: *,", "yaml: line 2: did not find expected alphabetic or numeric character"}, {"a: *b\n", "yaml: unknown anchor 'b' referenced"}, {"a: &a\n b: *a\n", "yaml: anchor 'a' value contains itself"}, + {"a: &x null\n<<:\n- *x\nb: &x {}\n", `yaml: map merge requires map or sequence of maps as the value`}, // Issue #529. {"value: -", "yaml: block sequence entries are not allowed in this context"}, {"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"}, {"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`}, {"{{.}}", `yaml: invalid map key: map\[interface\ \{\}\]interface \{\}\{".":interface \{\}\(nil\)\}`}, {"b: *a\na: &a {c: 1}", `yaml: unknown anchor 'a' referenced`}, {"%TAG !%79! tag:yaml.org,2002:\n---\nv: !%79!int '1'", "yaml: did not find expected whitespace"}, + { + "a: &a [00,00,00,00,00,00,00,00,00]\n" + + "b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]\n" + + "c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]\n" + + "d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]\n" + + "e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d]\n" + + "f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e]\n" + + "g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f]\n" + + "h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g]\n" + + "i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h]\n", + "yaml: document contains excessive aliasing", + }, } func (s *S) TestUnmarshalErrors(c *C) { @@ -854,6 +875,13 @@ func (s *S) TestUnmarshalErrors(c *C) { var value interface{} err := yaml.Unmarshal([]byte(item.data), &value) c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value)) + + if strings.Contains(item.data, ":") { + // Repeat test with typed value. + var value map[string]interface{} + err := yaml.Unmarshal([]byte(item.data), &value) + c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value)) + } } } diff --git a/vendor/gopkg.in/yaml.v2/encode.go b/vendor/gopkg.in/yaml.v2/encode.go index a14435e..0ee738e 100644 --- a/vendor/gopkg.in/yaml.v2/encode.go +++ b/vendor/gopkg.in/yaml.v2/encode.go @@ -13,6 +13,19 @@ import ( "unicode/utf8" ) +// jsonNumber is the interface of the encoding/json.Number datatype. +// Repeating the interface here avoids a dependency on encoding/json, and also +// supports other libraries like jsoniter, which use a similar datatype with +// the same interface. Detecting this interface is useful when dealing with +// structures containing json.Number, which is a string under the hood. The +// encoder should prefer the use of Int64(), Float64() and string(), in that +// order, when encoding this type. +type jsonNumber interface { + Float64() (float64, error) + Int64() (int64, error) + String() string +} + type encoder struct { emitter yaml_emitter_t event yaml_event_t @@ -89,6 +102,21 @@ func (e *encoder) marshal(tag string, in reflect.Value) { } iface := in.Interface() switch m := iface.(type) { + case jsonNumber: + integer, err := m.Int64() + if err == nil { + // In this case the json.Number is a valid int64 + in = reflect.ValueOf(integer) + break + } + float, err := m.Float64() + if err == nil { + // In this case the json.Number is a valid float64 + in = reflect.ValueOf(float) + break + } + // fallback case - no number could be obtained + in = reflect.ValueOf(m.String()) case time.Time, *time.Time: // Although time.Time implements TextMarshaler, // we don't want to treat it as a string for YAML diff --git a/vendor/gopkg.in/yaml.v2/encode_test.go b/vendor/gopkg.in/yaml.v2/encode_test.go index f0911a7..4a26600 100644 --- a/vendor/gopkg.in/yaml.v2/encode_test.go +++ b/vendor/gopkg.in/yaml.v2/encode_test.go @@ -15,6 +15,24 @@ import ( "gopkg.in/yaml.v2" ) +type jsonNumberT string + +func (j jsonNumberT) Int64() (int64, error) { + val, err := strconv.Atoi(string(j)) + if err != nil { + return 0, err + } + return int64(val), nil +} + +func (j jsonNumberT) Float64() (float64, error) { + return strconv.ParseFloat(string(j), 64) +} + +func (j jsonNumberT) String() string { + return string(j) +} + var marshalIntTest = 123 var marshalTests = []struct { @@ -367,6 +385,18 @@ var marshalTests = []struct { map[string]string{"a": "你好 #comment"}, "a: '你好 #comment'\n", }, + { + map[string]interface{}{"a": jsonNumberT("5")}, + "a: 5\n", + }, + { + map[string]interface{}{"a": jsonNumberT("100.5")}, + "a: 100.5\n", + }, + { + map[string]interface{}{"a": jsonNumberT("bogus")}, + "a: bogus\n", + }, } func (s *S) TestMarshal(c *C) { diff --git a/vendor/gopkg.in/yaml.v2/limit_test.go b/vendor/gopkg.in/yaml.v2/limit_test.go new file mode 100644 index 0000000..ba1c080 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/limit_test.go @@ -0,0 +1,113 @@ +package yaml_test + +import ( + "strings" + "testing" + + . "gopkg.in/check.v1" + "gopkg.in/yaml.v2" +) + +var limitTests = []struct { + name string + data []byte + error string +}{ + { + name: "1000kb of maps with 100 aliases", + data: []byte(`{a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-100) + `], b: &b [*a` + strings.Repeat(`,*a`, 99) + `]}`), + error: "yaml: document contains excessive aliasing", + }, { + name: "1000kb of deeply nested slices", + data: []byte(strings.Repeat(`[`, 1000*1024)), + error: "yaml: exceeded max depth of 10000", + }, { + name: "1000kb of deeply nested maps", + data: []byte("x: " + strings.Repeat(`{`, 1000*1024)), + error: "yaml: exceeded max depth of 10000", + }, { + name: "1000kb of deeply nested indents", + data: []byte(strings.Repeat(`- `, 1000*1024)), + error: "yaml: exceeded max depth of 10000", + }, { + name: "1000kb of 1000-indent lines", + data: []byte(strings.Repeat(strings.Repeat(`- `, 1000)+"\n", 1024/2)), + }, + {name: "1kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1*1024/4-1) + `]`)}, + {name: "10kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 10*1024/4-1) + `]`)}, + {name: "100kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 100*1024/4-1) + `]`)}, + {name: "1000kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-1) + `]`)}, +} + +func (s *S) TestLimits(c *C) { + if testing.Short() { + return + } + for _, tc := range limitTests { + var v interface{} + err := yaml.Unmarshal(tc.data, &v) + if len(tc.error) > 0 { + c.Assert(err, ErrorMatches, tc.error, Commentf("testcase: %s", tc.name)) + } else { + c.Assert(err, IsNil, Commentf("testcase: %s", tc.name)) + } + } +} + +func Benchmark1000KB100Aliases(b *testing.B) { + benchmark(b, "1000kb of maps with 100 aliases") +} +func Benchmark1000KBDeeplyNestedSlices(b *testing.B) { + benchmark(b, "1000kb of deeply nested slices") +} +func Benchmark1000KBDeeplyNestedMaps(b *testing.B) { + benchmark(b, "1000kb of deeply nested maps") +} +func Benchmark1000KBDeeplyNestedIndents(b *testing.B) { + benchmark(b, "1000kb of deeply nested indents") +} +func Benchmark1000KB1000IndentLines(b *testing.B) { + benchmark(b, "1000kb of 1000-indent lines") +} +func Benchmark1KBMaps(b *testing.B) { + benchmark(b, "1kb of maps") +} +func Benchmark10KBMaps(b *testing.B) { + benchmark(b, "10kb of maps") +} +func Benchmark100KBMaps(b *testing.B) { + benchmark(b, "100kb of maps") +} +func Benchmark1000KBMaps(b *testing.B) { + benchmark(b, "1000kb of maps") +} + +func benchmark(b *testing.B, name string) { + for _, t := range limitTests { + if t.name != name { + continue + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + var v interface{} + err := yaml.Unmarshal(t.data, &v) + if len(t.error) > 0 { + if err == nil { + b.Errorf("expected error, got none") + } else if err.Error() != t.error { + b.Errorf("expected error '%s', got '%s'", t.error, err.Error()) + } + } else { + if err != nil { + b.Errorf("unexpected error: %v", err) + } + } + } + + return + } + + b.Errorf("testcase %q not found", name) +} diff --git a/vendor/gopkg.in/yaml.v2/resolve.go b/vendor/gopkg.in/yaml.v2/resolve.go index 6c151db..4120e0c 100644 --- a/vendor/gopkg.in/yaml.v2/resolve.go +++ b/vendor/gopkg.in/yaml.v2/resolve.go @@ -81,7 +81,7 @@ func resolvableTag(tag string) bool { return false } -var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`) +var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) func resolve(tag string, in string) (rtag string, out interface{}) { if !resolvableTag(tag) { diff --git a/vendor/gopkg.in/yaml.v2/scannerc.go b/vendor/gopkg.in/yaml.v2/scannerc.go index 077fd1d..570b8ec 100644 --- a/vendor/gopkg.in/yaml.v2/scannerc.go +++ b/vendor/gopkg.in/yaml.v2/scannerc.go @@ -906,6 +906,9 @@ func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { return true } +// max_flow_level limits the flow_level +const max_flow_level = 10000 + // Increase the flow level and resize the simple key list if needed. func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { // Reset the simple key on the next level. @@ -913,6 +916,11 @@ func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { // Increase the flow level. parser.flow_level++ + if parser.flow_level > max_flow_level { + return yaml_parser_set_scanner_error(parser, + "while increasing flow level", parser.simple_keys[len(parser.simple_keys)-1].mark, + fmt.Sprintf("exceeded max depth of %d", max_flow_level)) + } return true } @@ -925,6 +933,9 @@ func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { return true } +// max_indents limits the indents stack size +const max_indents = 10000 + // Push the current indentation level to the stack and set the new level // the current column is greater than the indentation level. In this case, // append or insert the specified token into the token queue. @@ -939,6 +950,11 @@ func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml // indentation level. parser.indents = append(parser.indents, parser.indent) parser.indent = column + if len(parser.indents) > max_indents { + return yaml_parser_set_scanner_error(parser, + "while increasing indent level", parser.simple_keys[len(parser.simple_keys)-1].mark, + fmt.Sprintf("exceeded max depth of %d", max_indents)) + } // Create a token and insert it into the queue. token := yaml_token_t{