mirror of
https://github.com/Mic92/sops-nix.git
synced 2024-12-14 11:57:52 +00:00
Switch the libs to now external ones
This commit is contained in:
parent
6c916c1f57
commit
f636296aff
12 changed files with 22 additions and 474 deletions
|
@ -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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{ pkgs ? import <nixpkgs> {} }: 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
|
||||
|
|
6
go.mod
6
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
|
||||
|
|
12
go.sum
12
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=
|
||||
|
|
|
@ -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<<tobits - 1)
|
||||
for idx, value := range data {
|
||||
if value>>frombits != 0 {
|
||||
return nil, fmt.Errorf("invalid data range: data[%d]=%d (frombits=%d)", idx, value, frombits)
|
||||
}
|
||||
acc = acc<<frombits | uint32(value)
|
||||
bits += frombits
|
||||
for bits >= 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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue