Skip to content

Commit

Permalink
Merge pull request #121 from neicnordic/bugfix/unencrypted_private_key
Browse files Browse the repository at this point in the history
Add support for unencrypted private crypt4gh keys
  • Loading branch information
blankdots authored Feb 27, 2024
2 parents 06c733a + 89e3897 commit c088dab
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 20 deletions.
2 changes: 1 addition & 1 deletion internal/version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

// The version in the current branch
var Version = "1.9.0"
var Version = "1.9.1"

// If this is "" (empty string) then it means that it is a final release.
// Otherwise, this is a pre-release e.g. "dev", "beta", "rc1", etc.
Expand Down
60 changes: 41 additions & 19 deletions keys/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,28 +121,58 @@ func readCrypt4GHPrivateKey(pemBytes, passPhrase []byte) (privateKey [chacha20po
}
var rounds uint32
var salt []byte
kdfunction, ok := kdf.KDFS[string(kdfName)]
if !ok {
return privateKey, fmt.Errorf("KDF %v not supported", string(kdfName))
}
if string(kdfName) != "none" {
if passPhrase == nil {
return privateKey, errors.New("private key is password-protected, need a password for decryption")
}

if string(kdfName) == none {
// Unencrypted private key

err = binary.Read(buffer, binary.BigEndian, &length)
if err != nil {
return
}
err = binary.Read(buffer, binary.BigEndian, &rounds)
ciphername := make([]byte, length)
err = binary.Read(buffer, binary.BigEndian, &ciphername)
if err != nil {
return
}
salt = make([]byte, length-4)
err = binary.Read(buffer, binary.BigEndian, &salt)

if string(ciphername) != none {
return privateKey, errors.New("invalid private key: KDF is 'none', but cipher is not 'none'")
}

err = binary.Read(buffer, binary.BigEndian, &length)
if err != nil {
return
}
payload := make([]byte, length)
err = binary.Read(buffer, binary.BigEndian, &payload)

copy(privateKey[:], payload)

return
}
if passPhrase == nil {
return privateKey, errors.New("private key is password-protected, need a password for decryption")
}

kdfunction, ok := kdf.KDFS[string(kdfName)]
if !ok {
return privateKey, fmt.Errorf("KDF %v not supported", string(kdfName))
}

err = binary.Read(buffer, binary.BigEndian, &length)
if err != nil {
return
}
err = binary.Read(buffer, binary.BigEndian, &rounds)
if err != nil {
return
}
salt = make([]byte, length-4)
err = binary.Read(buffer, binary.BigEndian, &salt)
if err != nil {
return
}

err = binary.Read(buffer, binary.BigEndian, &length)
if err != nil {
return
Expand All @@ -161,14 +191,6 @@ func readCrypt4GHPrivateKey(pemBytes, passPhrase []byte) (privateKey [chacha20po
if err != nil {
return
}
if string(kdfName) == none {
if string(ciphername) != none {
return privateKey, errors.New("invalid private key: KDF is 'none', but cipher is not 'none'")
}
copy(privateKey[:], payload)

return
}
if string(ciphername) != supportedCipherName {
return privateKey, fmt.Errorf("unsupported key encryption: %v", string(ciphername))
}
Expand Down
13 changes: 13 additions & 0 deletions keys/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ ECAwQ=
-----END OPENSSH PRIVATE KEY-----
`

const unencryptedCrypt4GH = `-----BEGIN CRYPT4GH PRIVATE KEY-----
YzRnaC12MQAEbm9uZQAEbm9uZQAgCvMeraG2L8NC9rDji46RXESWcXkoV5JeF0IiJdvzyhQ=
-----END CRYPT4GH PRIVATE KEY-----
`

const sshEd25519Pub = `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGShUtbgxD70Gj+alwupjPHpTeIHf/s7pWNfx10VvYHV [email protected]
`

Expand Down Expand Up @@ -100,6 +105,14 @@ func TestReadKey(t *testing.T) {
passPhrase []byte
hash string
}{
{
name: "unencrypted.sec.pem",
content: unencryptedCrypt4GH,
readPrivateKeyFunction: ReadPrivateKey,
readPublicKeyFunction: nil,
passPhrase: nil,
hash: "0af31eada1b62fc342f6b0e38b8e915c449671792857925e17422225dbf3ca14",
},
{
name: "crypt4gh-x25519-enc.sec.pem",
content: crypt4ghX25519Sec,
Expand Down
72 changes: 72 additions & 0 deletions streaming/streaming_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ YzRnaC12MQAGc2NyeXB0ABQAAAAA2l23+H3w2F3/Zylx5Gs2CwARY2hhY2hhMjBfcG9seTEzMDUAPOdx
-----END CRYPT4GH PRIVATE KEY-----
`

const unencryptedSec = `-----BEGIN CRYPT4GH PRIVATE KEY-----
YzRnaC12MQAEbm9uZQAEbm9uZQAgmWET98yM/kaM27VkZGRktSR1q/htHspqBlzEL0FRD3g=
-----END CRYPT4GH PRIVATE KEY-----
`

const unencryptedPub = `-----BEGIN CRYPT4GH PUBLIC KEY-----
4AOAKwiwvkjF6Wvoh9Aw5gUKjoOoRcA5svJadKzEDhM=
-----END CRYPT4GH PUBLIC KEY-----
`

func readerToReader(seekable bool, source io.Reader) io.Reader {
if seekable {
return source
Expand Down Expand Up @@ -1325,3 +1335,65 @@ func TestSomeEOFs(t *testing.T) {
}
}
}

func TestUnencryptedPrivate(t *testing.T) {
for _, seekable := range []bool{true, false} {
inFile, err := os.Open("../test/sample.txt")
if err != nil {
t.Error(err)
}

inBytes, err := io.ReadAll(inFile)
if err != nil {
t.Error(err)
}

if err = inFile.Close(); err != nil {
t.Error(err)
}

readerPublicKey, err := keys.ReadPublicKey(strings.NewReader(unencryptedPub))
if err != nil {
t.Error(err)
}
buffer := bytes.Buffer{}

readerPublicKeyList := [][chacha20poly1305.KeySize]byte{}
readerPublicKeyList = append(readerPublicKeyList, readerPublicKey)

writer, err := NewCrypt4GHWriterWithoutPrivateKey(&buffer, readerPublicKeyList, nil)
if err != nil {
t.Error(err)
}

if r, err := writer.Write(inBytes[:20000]); err != nil || r != 20000 {
t.Errorf("Problem when writing to cryptgh writer, r=%d, err=%v", r, err)
}

err = writer.Close()
if err != nil {
t.Error(err)
}
readerSecretKey, err := keys.ReadPrivateKey(strings.NewReader(unencryptedSec), nil)
if err != nil {
t.Error(err)
}

bufferReader := bytes.NewReader(buffer.Bytes())

reader, err := NewCrypt4GHReader(readerToReader(seekable, bufferReader), readerSecretKey, nil)
if err != nil {
t.Error(err)
}

buf := make([]byte, 16384)
r, err := reader.Read(buf)
if err != nil || r != len(buf) {
t.Errorf("Read returned unexpected r=%v, err=%v", r, err)
}

if !bytes.Equal(buf, inBytes[:16384]) {
t.Errorf("Content mismatch when passing segment boundary")
}
}
}

0 comments on commit c088dab

Please sign in to comment.