1
0
Fork 0
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:
Janne Heß 2021-09-01 13:32:03 +02:00
parent 6c916c1f57
commit f636296aff
No known key found for this signature in database
GPG key ID: 69165158F05265DF
12 changed files with 22 additions and 474 deletions

View file

@ -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

View 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
View file

@ -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
View file

@ -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=

View file

@ -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
}

View file

@ -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")
}
}
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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;
};
}

View file

@ -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)
}

View file

@ -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;
};
}

View file

@ -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)
}
}