From f636296aff0adb3a620d4125e8e8c1a2443ad2cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janne=20He=C3=9F?= Date: Wed, 1 Sep 2021 13:32:03 +0200 Subject: [PATCH] Switch the libs to now external ones --- README.md | 8 +- default.nix | 4 +- go.mod | 6 +- go.sum | 12 +- pkgs/bech32/bech32.go | 179 -------------------- pkgs/bech32/bech32_test.go | 94 ---------- pkgs/sops-install-secrets/agessh/convert.go | 45 ----- pkgs/sops-install-secrets/main.go | 8 +- pkgs/ssh-privkey-to-age/default.nix | 19 --- pkgs/ssh-privkey-to-age/main.go | 28 --- pkgs/ssh-pubkey-to-age/default.nix | 19 --- pkgs/ssh-pubkey-to-age/main.go | 74 -------- 12 files changed, 22 insertions(+), 474 deletions(-) delete mode 100644 pkgs/bech32/bech32.go delete mode 100644 pkgs/bech32/bech32_test.go delete mode 100644 pkgs/sops-install-secrets/agessh/convert.go delete mode 100644 pkgs/ssh-privkey-to-age/default.nix delete mode 100644 pkgs/ssh-privkey-to-age/main.go delete mode 100644 pkgs/ssh-pubkey-to-age/default.nix delete mode 100644 pkgs/ssh-pubkey-to-age/main.go diff --git a/README.md b/README.md index 9ed284a..143656f 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ $ ssh-keygen -t ed25519 Converting the public key to the age format works like this: ```console -$ nix run -f default.nix ssh-pubkey-to-age -c sh -c 'ssh-add -L | ssh-pubkey-to-age' +$ nix-shell -p ssh-pubkey-to-age --run "ssh-add -L | ssh-pubkey-to-age" ``` Ssh public key files may also be piped into the `ssh-pubkey-to-age` tool. @@ -212,7 +212,7 @@ Ssh public key files may also be piped into the `ssh-pubkey-to-age` tool. Finally, you need to convert your private key to the age format: ```console $ mkdir -p ~/.config/sops -$ nix run -f default.nix ssh-privkey-to-age -c ssh-privkey-to-age ~/.ssh/id_ed25519 > ~/.config/sops/keys.txt +$ nix-shell -p ssh-privkey-to-age --run "ssh-privkey-to-age ~/.ssh/id_ed25519 > ~/.config/sops/age/keys.txt" ``` ### 3a. Get a PGP Public key for your machine @@ -252,8 +252,8 @@ If you prefer having a separate GnuPG key, see [Use with GnuPG instead of ssh ke The `ssh-pubkey-to-age` tool is used to convert any ssh public key to the age format. This way you can convert any key: ```console -$ nix run -f default.nix ssh-pubkey-to-age -c sh -c 'ssh-keyscan my-server.com | ssh-pubkey-to-age' -$ nix run -f default.nix ssh-pubkey-to-age -c sh -c 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-pubkey-to-age' +$ nix-shell -p ssh-pubkey-to-age --run 'ssh-keyscan my-server.com | ssh-pubkey-to-age' +$ nix-shell -p ssh-pubkey-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-pubkey-to-age' ``` ### 4. Create a sops file diff --git a/default.nix b/default.nix index 3300749..acd4424 100644 --- a/default.nix +++ b/default.nix @@ -1,5 +1,5 @@ { pkgs ? import {} }: let - vendorSha256 = "sha256:1bizqlj56lka37gbvm37p3yifn7w2z9kfhv486gv40wknzqclq12"; + vendorSha256 = "sha256:145vmy70mkbciy8mr92nz6w0rwr83884wwwjy4wa587c6wkgs3w2"; sops-install-secrets = pkgs.callPackage ./pkgs/sops-install-secrets { inherit vendorSha256; @@ -11,8 +11,6 @@ in rec { Also see https://github.com/Mic92/sops-nix/issues/98 '' pkgs.callPackage ./pkgs/sops-pgp-hook { }; sops-import-keys-hook = pkgs.callPackage ./pkgs/sops-import-keys-hook { }; - ssh-pubkey-to-age = pkgs.callPackage ./pkgs/ssh-pubkey-to-age { inherit vendorSha256; }; - ssh-privkey-to-age = pkgs.callPackage ./pkgs/ssh-privkey-to-age { inherit vendorSha256; }; inherit sops-install-secrets; # backwards compatibility diff --git a/go.mod b/go.mod index 6d92ef3..0e74acb 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module github.com/Mic92/sops-nix go 1.14 require ( - filippo.io/age v1.0.0-rc.3 + github.com/Mic92/ssh-to-age v0.0.0-20210829164312-1fe15380abe4 github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c github.com/mozilla-services/yaml v0.0.0-20191106225358-5c216288813c go.mozilla.org/sops/v3 v3.7.1 - golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 - golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43 + golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 + golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e ) // see https://github.com/mozilla/sops/pull/925 diff --git a/go.sum b/go.sum index 1726f6a..4567aa8 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ filippo.io/age v1.0.0-rc.3 h1:8JjuJ5ffGKDmC4SS0zoyQxZROZX75so768b7AjulKLw= filippo.io/age v1.0.0-rc.3/go.mod h1:UjINLBMeA60aGZkHCGsmDzKcaXoTTzpvrqQM+Vo3YHU= filippo.io/edwards25519 v1.0.0-alpha.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= filippo.io/edwards25519 v1.0.0-beta.3/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= +filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= +filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= github.com/Azure/azure-sdk-for-go v31.2.0+incompatible h1:kZFnTLmdQYNGfakatSivKHUfUnDZhqNdchHD4oIhp5k= github.com/Azure/azure-sdk-for-go v31.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= @@ -41,6 +43,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Mic92/sops/v3 v3.7.2-0.20210829155005-a7cbb9ffe599 h1:3UXsFmgei0TO2rwvIpW0EQNVJi4SnB3V3GgC/TGndzE= github.com/Mic92/sops/v3 v3.7.2-0.20210829155005-a7cbb9ffe599/go.mod h1:n1KOOXQUp7PbUIYr0yEExC6RWv2hjvQKLNufdWYLNQg= +github.com/Mic92/ssh-to-age v0.0.0-20210829164312-1fe15380abe4 h1:PRxM012OHgeUA3yH/FHbyl/q7ep0l1w7t+haJTw6Ozs= +github.com/Mic92/ssh-to-age v0.0.0-20210829164312-1fe15380abe4/go.mod h1:Y8QV5VlnhcBamWQHRdVNiqSuip5CDtJdmHxwQvHiXWs= github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= @@ -276,8 +280,9 @@ golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -333,8 +338,11 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43 h1:SgQ6LNaYJU0JIuEHv9+s6EbhSCwYeAf5Yvj6lpYlqAE= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e h1:XMgFehsDnnLGtjvjOfqWSUzt0alpTR1RSEuznObga2c= +golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/pkgs/bech32/bech32.go b/pkgs/bech32/bech32.go deleted file mode 100644 index 29310d4..0000000 --- a/pkgs/bech32/bech32.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) 2017 Takatoshi Nakagawa -// Copyright (c) 2019 Google LLC -// -// 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 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. - -// Package bech32 is a modified version of the reference implementation of BIP173. -package bech32 - -import ( - "fmt" - "strings" -) - -var charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" - -var generator = []uint32{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} - -func polymod(values []byte) uint32 { - chk := uint32(1) - for _, v := range values { - top := chk >> 25 - chk = (chk & 0x1ffffff) << 5 - chk = chk ^ uint32(v) - for i := 0; i < 5; i++ { - bit := top >> i & 1 - if bit == 1 { - chk ^= generator[i] - } - } - } - return chk -} - -func hrpExpand(hrp string) []byte { - h := []byte(strings.ToLower(hrp)) - var ret []byte - for _, c := range h { - ret = append(ret, c>>5) - } - ret = append(ret, 0) - for _, c := range h { - ret = append(ret, c&31) - } - return ret -} - -func verifyChecksum(hrp string, data []byte) bool { - return polymod(append(hrpExpand(hrp), data...)) == 1 -} - -func createChecksum(hrp string, data []byte) []byte { - values := append(hrpExpand(hrp), data...) - values = append(values, []byte{0, 0, 0, 0, 0, 0}...) - mod := polymod(values) ^ 1 - ret := make([]byte, 6) - for p := range ret { - shift := 5 * (5 - p) - ret[p] = byte(mod>>shift) & 31 - } - return ret -} - -func convertBits(data []byte, frombits, tobits byte, pad bool) ([]byte, error) { - var ret []byte - acc := uint32(0) - bits := byte(0) - maxv := byte(1<>frombits != 0 { - return nil, fmt.Errorf("invalid data range: data[%d]=%d (frombits=%d)", idx, value, frombits) - } - acc = acc<= tobits { - bits -= tobits - ret = append(ret, byte(acc>>bits)&maxv) - } - } - if pad { - if bits > 0 { - ret = append(ret, byte(acc<<(tobits-bits))&maxv) - } - } else if bits >= frombits { - return nil, fmt.Errorf("illegal zero padding") - } else if byte(acc<<(tobits-bits))&maxv != 0 { - return nil, fmt.Errorf("non-zero padding") - } - return ret, nil -} - -// Encode encodes the HRP and a bytes slice to Bech32. If the HRP is uppercase, -// the output will be uppercase. -func Encode(hrp string, data []byte) (string, error) { - values, err := convertBits(data, 8, 5, true) - if err != nil { - return "", err - } - if len(hrp)+len(values)+7 > 90 { - return "", fmt.Errorf("too long: hrp length=%d, data length=%d", len(hrp), len(values)) - } - if len(hrp) < 1 { - return "", fmt.Errorf("invalid HRP: %q", hrp) - } - for p, c := range hrp { - if c < 33 || c > 126 { - return "", fmt.Errorf("invalid HRP character: hrp[%d]=%d", p, c) - } - } - if strings.ToUpper(hrp) != hrp && strings.ToLower(hrp) != hrp { - return "", fmt.Errorf("mixed case HRP: %q", hrp) - } - lower := strings.ToLower(hrp) == hrp - hrp = strings.ToLower(hrp) - var ret strings.Builder - ret.WriteString(hrp) - ret.WriteString("1") - for _, p := range values { - ret.WriteByte(charset[p]) - } - for _, p := range createChecksum(hrp, values) { - ret.WriteByte(charset[p]) - } - if lower { - return ret.String(), nil - } - return strings.ToUpper(ret.String()), nil -} - -// Decode decodes a Bech32 string. If the string is uppercase, the HRP will be uppercase. -func Decode(s string) (hrp string, data []byte, err error) { - if len(s) > 90 { - return "", nil, fmt.Errorf("too long: len=%d", len(s)) - } - if strings.ToLower(s) != s && strings.ToUpper(s) != s { - return "", nil, fmt.Errorf("mixed case") - } - pos := strings.LastIndex(s, "1") - if pos < 1 || pos+7 > len(s) { - return "", nil, fmt.Errorf("separator '1' at invalid position: pos=%d, len=%d", pos, len(s)) - } - hrp = s[:pos] - for p, c := range hrp { - if c < 33 || c > 126 { - return "", nil, fmt.Errorf("invalid character human-readable part: s[%d]=%d", p, c) - } - } - s = strings.ToLower(s) - for p, c := range s[pos+1:] { - d := strings.IndexRune(charset, c) - if d == -1 { - return "", nil, fmt.Errorf("invalid character data part: s[%d]=%v", p, c) - } - data = append(data, byte(d)) - } - if !verifyChecksum(hrp, data) { - return "", nil, fmt.Errorf("invalid checksum") - } - data, err = convertBits(data[:len(data)-6], 5, 8, false) - if err != nil { - return "", nil, err - } - return hrp, data, nil -} diff --git a/pkgs/bech32/bech32_test.go b/pkgs/bech32/bech32_test.go deleted file mode 100644 index 72f1ab6..0000000 --- a/pkgs/bech32/bech32_test.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2013-2017 The btcsuite developers -// Copyright (c) 2016-2017 The Lightning Network Developers -// Copyright (c) 2019 Google LLC -// -// Permission to use, copy, modify, and 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. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -package bech32_test - -import ( - "strings" - "testing" - - "filippo.io/age/internal/bech32" -) - -func TestBech32(t *testing.T) { - tests := []struct { - str string - valid bool - }{ - {"A12UEL5L", true}, - {"a12uel5l", true}, - {"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", true}, - {"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", true}, - {"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", true}, - {"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", true}, - - // invalid checksum - {"split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w", false}, - // invalid character (space) in hrp - {"s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p", false}, - {"split1cheo2y9e2w", false}, // invalid character (o) in data part - {"split1a2y9w", false}, // too short data part - {"1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", false}, // empty hrp - // invalid character (DEL) in hrp - {"spl" + string(rune(127)) + "t1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", false}, - // too long - {"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", false}, - - // BIP 173 invalid vectors. - {"an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", false}, - {"pzry9x0s0muk", false}, - {"1pzry9x0s0muk", false}, - {"x1b4n0q5v", false}, - {"li1dgmt3", false}, - {"de1lg7wt\xff", false}, - {"A1G7SGD8", false}, - {"10a06t8", false}, - {"1qzzfhee", false}, - } - - for _, test := range tests { - str := test.str - hrp, decoded, err := bech32.Decode(str) - if !test.valid { - // Invalid string decoding should result in error. - if err == nil { - t.Errorf("expected decoding to fail for invalid string %v", test.str) - } - continue - } - - // Valid string decoding should result in no error. - if err != nil { - t.Errorf("expected string to be valid bech32: %v", err) - } - - // Check that it encodes to the same string. - encoded, err := bech32.Encode(hrp, decoded) - if err != nil { - t.Errorf("encoding failed: %v", err) - } - if encoded != str { - t.Errorf("expected data to encode to %v, but got %v", str, encoded) - } - - // Flip a bit in the string an make sure it is caught. - pos := strings.LastIndexAny(str, "1") - flipped := str[:pos+1] + string((str[pos+1] ^ 1)) + str[pos+2:] - if _, _, err = bech32.Decode(flipped); err == nil { - t.Error("expected decoding to fail") - } - } -} diff --git a/pkgs/sops-install-secrets/agessh/convert.go b/pkgs/sops-install-secrets/agessh/convert.go deleted file mode 100644 index e9b978f..0000000 --- a/pkgs/sops-install-secrets/agessh/convert.go +++ /dev/null @@ -1,45 +0,0 @@ -package agessh - -import ( - "crypto/ed25519" - "crypto/sha512" - "fmt" - "reflect" - "strings" - - "github.com/Mic92/sops-nix/pkgs/bech32" - "golang.org/x/crypto/curve25519" - "golang.org/x/crypto/ssh" -) - -func ed25519PrivateKeyToCurve25519(pk ed25519.PrivateKey) ([]byte, error) { - h := sha512.New() - _, err := h.Write(pk.Seed()) - if err != nil { - return []byte{}, err - } - out := h.Sum(nil) - return out[:curve25519.ScalarSize], nil -} - -func SSHPrivateKeyToBech32(sshPrivateKey []byte) (string, error) { - privateKey, err := ssh.ParseRawPrivateKey(sshPrivateKey) - if err != nil { - return "", err - } - - ed25519Key, ok := privateKey.(*ed25519.PrivateKey) - if !ok { - return "", fmt.Errorf("Only ED25519 keys are supported, got: %s", reflect.TypeOf(privateKey)) - } - bytes, err := ed25519PrivateKeyToCurve25519(*ed25519Key) - if err != nil { - return "", err - } - - s, err := bech32.Encode("AGE-SECRET-KEY-", bytes) - if err != nil { - return "", err - } - return strings.ToUpper(s), nil -} diff --git a/pkgs/sops-install-secrets/main.go b/pkgs/sops-install-secrets/main.go index d3fc5cb..f42d1bd 100644 --- a/pkgs/sops-install-secrets/main.go +++ b/pkgs/sops-install-secrets/main.go @@ -17,8 +17,8 @@ import ( "syscall" "time" - "github.com/Mic92/sops-nix/pkgs/sops-install-secrets/agessh" "github.com/Mic92/sops-nix/pkgs/sops-install-secrets/sshkeys" + agessh "github.com/Mic92/ssh-to-age" "github.com/mozilla-services/yaml" "go.mozilla.org/sops/v3/decrypt" @@ -533,13 +533,13 @@ func importAgeSSHKeys(keyPaths []string, ageFilePath string) error { if err != nil { return fmt.Errorf("Cannot read ssh key '%s': %w", p, err) } - // Convert the key to bech32 - bech32, err := agessh.SSHPrivateKeyToBech32(sshKey) + // Convert the key to age + bech32, err := agessh.SSHPrivateKeyToAge(sshKey) if err != nil { return fmt.Errorf("Cannot convert ssh key '%s': %w", p, err) } // Append it to the file - _, err = ageFile.WriteString(bech32 + "\n") + _, err = ageFile.WriteString(*bech32 + "\n") if err != nil { return fmt.Errorf("Cannot write key to age file: %w", err) } diff --git a/pkgs/ssh-privkey-to-age/default.nix b/pkgs/ssh-privkey-to-age/default.nix deleted file mode 100644 index 72777fa..0000000 --- a/pkgs/ssh-privkey-to-age/default.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ stdenv, lib, buildGoModule, path, pkgs, vendorSha256, go }: -buildGoModule { - pname = "ssh-privkey-to-age"; - version = "0.0.1"; - - src = ../..; - - subPackages = [ "pkgs/ssh-privkey-to-age" ]; - - inherit vendorSha256; - - meta = with lib; { - description = "Converter that converts SSH private keys into age keys"; - homepage = "https://github.com/Mic92/sops-nix"; - license = licenses.mit; - maintainers = with maintainers; [ mic92 ]; - platforms = platforms.linux; - }; -} diff --git a/pkgs/ssh-privkey-to-age/main.go b/pkgs/ssh-privkey-to-age/main.go deleted file mode 100644 index d619bba..0000000 --- a/pkgs/ssh-privkey-to-age/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "os" - - "github.com/Mic92/sops-nix/pkgs/sops-install-secrets/agessh" -) - -func main() { - if len(os.Args) != 2 { - println("Usage: " + os.Args[0] + " [path to ssh private key]") - os.Exit(1) - } - - sshKey, err := ioutil.ReadFile(os.Args[1]) - if err != nil { - panic(fmt.Errorf("Cannot read ssh key '%s': %w", os.Args[1], err)) - } - - // Convert the key to bech32 - bech32, err := agessh.SSHPrivateKeyToBech32(sshKey) - if err != nil { - panic(fmt.Errorf("Cannot convert ssh key '%s': %w", os.Args[1], err)) - } - fmt.Println(bech32) -} diff --git a/pkgs/ssh-pubkey-to-age/default.nix b/pkgs/ssh-pubkey-to-age/default.nix deleted file mode 100644 index ca37518..0000000 --- a/pkgs/ssh-pubkey-to-age/default.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ stdenv, lib, buildGoModule, path, pkgs, vendorSha256, go }: -buildGoModule { - pname = "ssh-pubkey-to-age"; - version = "0.0.1"; - - src = ../..; - - subPackages = [ "pkgs/ssh-pubkey-to-age" ]; - - inherit vendorSha256; - - meta = with lib; { - description = "Converter that converts SSH public keys into age keys"; - homepage = "https://github.com/Mic92/sops-nix"; - license = licenses.mit; - maintainers = with maintainers; [ mic92 ]; - platforms = platforms.linux; - }; -} diff --git a/pkgs/ssh-pubkey-to-age/main.go b/pkgs/ssh-pubkey-to-age/main.go deleted file mode 100644 index c725759..0000000 --- a/pkgs/ssh-pubkey-to-age/main.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "bufio" - "crypto/ed25519" - "errors" - "fmt" - "os" - "strings" - - "filippo.io/edwards25519" - "github.com/Mic92/sops-nix/pkgs/bech32" - "golang.org/x/crypto/ssh" -) - -func ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) ([]byte, error) { - // See https://blog.filippo.io/using-ed25519-keys-for-encryption and - // https://pkg.go.dev/filippo.io/edwards25519#Point.BytesMontgomery. - p, err := new(edwards25519.Point).SetBytes(pk) - if err != nil { - return nil, err - } - return p.BytesMontgomery(), nil -} - -func main() { - if len(os.Args) != 1 { - println("Usage: " + os.Args[0]) - println("Pipe a SSH public key or the output of ssh-keyscan into it") - os.Exit(1) - } - - scanner := bufio.NewScanner(os.Stdin) - for scanner.Scan() { - text := scanner.Text() + "\n" - var err error - var pk ssh.PublicKey - if strings.HasPrefix(text, "ssh-") { - pk, _, _, _, err = ssh.ParseAuthorizedKey([]byte(text)) - } else { - _, _, pk, _, _, err = ssh.ParseKnownHosts([]byte(text)) - } - if err != nil { - panic(err) - } - // We only care about ed25519 - if pk.Type() != ssh.KeyAlgoED25519 { - continue - } - // Get the bytes - cpk, ok := pk.(ssh.CryptoPublicKey) - if !ok { - panic(errors.New("pk does not implement ssh.CryptoPublicKey")) - } - epk, ok := cpk.CryptoPublicKey().(ed25519.PublicKey) - if !ok { - panic(errors.New("unexpected public key type")) - } - // Convert the key to curve ed25519 - mpk, err := ed25519PublicKeyToCurve25519(epk) - if err != nil { - panic(fmt.Errorf("invalid Ed25519 public key: %v", err)) - } - // Encode the key - s, err := bech32.Encode("age", mpk) - if err != nil { - panic(err) - } - fmt.Println(s) - } - if err := scanner.Err(); err != nil { - panic(err) - } -}