From 3c79f40acb8b9ce45361e6d18b6121eda1ba5169 Mon Sep 17 00:00:00 2001 From: wussler Date: Tue, 7 Apr 2020 14:59:25 +0200 Subject: [PATCH] Add SHA256 fingerprint utils and helpers (#41) --- crypto/key.go | 24 ++++++++++++++++++++++-- crypto/key_test.go | 13 +++++++++++++ go.mod | 2 +- go.sum | 5 +++++ helper/key.go | 9 +++++++++ helper/key_test.go | 18 ++++++++++++++++++ helper/mobile.go | 11 +++++++++++ helper/mobile_test.go | 9 +++++++++ 8 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 helper/key_test.go diff --git a/crypto/key.go b/crypto/key.go index 5005ea1..5e1ee43 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -3,6 +3,7 @@ package crypto import ( "bytes" "crypto" + "crypto/sha256" "encoding/hex" "fmt" "io" @@ -14,9 +15,9 @@ import ( "github.com/ProtonMail/gopenpgp/v2/constants" "github.com/pkg/errors" - "golang.org/x/crypto/openpgp" + openpgp "golang.org/x/crypto/openpgp" xarmor "golang.org/x/crypto/openpgp/armor" - "golang.org/x/crypto/openpgp/packet" + packet "golang.org/x/crypto/openpgp/packet" ) // Key contains a single private or public key @@ -321,8 +322,27 @@ func (key *Key) GetFingerprint() string { return hex.EncodeToString(key.entity.PrimaryKey.Fingerprint[:]) } +// GetSHA256Fingerprints computes the SHA256 fingerprints of the key and subkeys +func (key *Key) GetSHA256Fingerprints() (fingerprints []string) { + fingerprints = append(fingerprints, hex.EncodeToString(getSHA256FingerprintBytes(key.entity.PrimaryKey))) + for _, sub := range key.entity.Subkeys { + fingerprints = append(fingerprints, hex.EncodeToString(getSHA256FingerprintBytes(sub.PublicKey))) + } + return +} + // --- Internal methods +// getSHA256FingerprintBytes computes the SHA256 fingerprint of a public key object +func getSHA256FingerprintBytes(pk *packet.PublicKey) []byte { + fingerPrint := sha256.New() + + // Hashing can't return an error, and has already been done when parsing the key, + // hence the error is nil + _ = pk.SerializeForHash(fingerPrint) + return fingerPrint.Sum(nil) +} + // readFrom reads unarmored and armored keys from r and adds them to the keyring. func (key *Key) readFrom(r io.Reader, armored bool) error { var err error diff --git a/crypto/key_test.go b/crypto/key_test.go index 17a0f94..0d09e9b 100644 --- a/crypto/key_test.go +++ b/crypto/key_test.go @@ -310,3 +310,16 @@ func TestGetArmoredPublicKey(t *testing.T) { assert.Exactly(t, eb, b) } + +func TestGetSHA256FingerprintsV4(t *testing.T) { + publicKey, err := NewKeyFromArmored(readTestFile("keyring_publicKey", false)) + if err != nil { + t.Fatal("Cannot unarmor key:", err) + } + + sha256Fingerprints := publicKey.GetSHA256Fingerprints() + + assert.Len(t, sha256Fingerprints, 2) + assert.Exactly(t, "d9ac0b857da6d2c8be985b251a9e3db31e7a1d2d832d1f07ebe838a9edce9c24", sha256Fingerprints[0]) + assert.Exactly(t, "203dfba1f8442c17e59214d9cd11985bfc5cc8721bb4a71740dd5507e58a1a0d", sha256Fingerprints[1]) +} diff --git a/go.mod b/go.mod index 1793e28..10765d8 100644 --- a/go.mod +++ b/go.mod @@ -9,4 +9,4 @@ require ( golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 ) -replace golang.org/x/crypto => github.com/ProtonMail/crypto v0.0.0-20191122234321-e77a1f03baa0 +replace golang.org/x/crypto => github.com/ProtonMail/crypto v0.0.0-20200406171318-1e8bf1c40869 diff --git a/go.sum b/go.sum index b1cb6b7..5a3672d 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,10 @@ github.com/ProtonMail/crypto v0.0.0-20191122234321-e77a1f03baa0 h1:mCww5Yl0Pm4PZPSooupyWDgihrh96p6+O4PY1hs0FBw= github.com/ProtonMail/crypto v0.0.0-20191122234321-e77a1f03baa0/go.mod h1:MBriIAodHvZ+YvwvMJWCTmseW/LkeVRPWp/iZKvee4g= +github.com/ProtonMail/crypto v0.0.0-20200406132833-0ff9e5f60587 h1:tRJioacMtJwCdlCHRaUl/2VfVNazf+ypT+SV5eiLnVY= +github.com/ProtonMail/crypto v0.0.0-20200406132833-0ff9e5f60587/go.mod h1:MBriIAodHvZ+YvwvMJWCTmseW/LkeVRPWp/iZKvee4g= +github.com/ProtonMail/crypto v0.0.0-20200406145536-081a35660375/go.mod h1:MBriIAodHvZ+YvwvMJWCTmseW/LkeVRPWp/iZKvee4g= +github.com/ProtonMail/crypto v0.0.0-20200406171318-1e8bf1c40869 h1:GtxsVknmfEUppe8lCX/Xgs+BbCzXf9bdH8+eBbtGO70= +github.com/ProtonMail/crypto v0.0.0-20200406171318-1e8bf1c40869/go.mod h1:MBriIAodHvZ+YvwvMJWCTmseW/LkeVRPWp/iZKvee4g= github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a h1:W6RrgN/sTxg1msqzFFb+G80MFmpjMw61IU+slm+wln4= github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/helper/key.go b/helper/key.go index 8d1d0d6..a1a695b 100644 --- a/helper/key.go +++ b/helper/key.go @@ -46,3 +46,12 @@ func GenerateKey(name, email string, passphrase []byte, keyType string, bits int key.ClearPrivateParams() return locked.Armor() } + +func GetSHA256Fingerprints(publicKey string) ([]string, error) { + key, err := crypto.NewKeyFromArmored(publicKey) + if err != nil { + return nil, err + } + + return key.GetSHA256Fingerprints(), nil +} diff --git a/helper/key_test.go b/helper/key_test.go new file mode 100644 index 0000000..08d6986 --- /dev/null +++ b/helper/key_test.go @@ -0,0 +1,18 @@ +package helper + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetSHA256FingerprintsV4(t *testing.T) { + sha256Fingerprints, err := GetSHA256Fingerprints(readTestFile("keyring_publicKey", false)) + if err != nil { + t.Fatal("Cannot unarmor key:", err) + } + + assert.Len(t, sha256Fingerprints, 2) + assert.Exactly(t, "d9ac0b857da6d2c8be985b251a9e3db31e7a1d2d832d1f07ebe838a9edce9c24", sha256Fingerprints[0]) + assert.Exactly(t, "203dfba1f8442c17e59214d9cd11985bfc5cc8721bb4a71740dd5507e58a1a0d", sha256Fingerprints[1]) +} diff --git a/helper/mobile.go b/helper/mobile.go index f2360e7..825cca3 100644 --- a/helper/mobile.go +++ b/helper/mobile.go @@ -1,6 +1,8 @@ package helper import ( + "encoding/json" + "github.com/ProtonMail/gopenpgp/v2/crypto" ) @@ -65,3 +67,12 @@ func EncryptAttachment(plainData []byte, fileName string, keyRing *crypto.KeyRin } return decrypted, nil } + +func GetJsonSHA256Fingerprints(publicKey string) ([]byte, error) { + key, err := crypto.NewKeyFromArmored(publicKey) + if err != nil { + return nil, err + } + + return json.Marshal(key.GetSHA256Fingerprints()) +} diff --git a/helper/mobile_test.go b/helper/mobile_test.go index fd6f828..49d4136 100644 --- a/helper/mobile_test.go +++ b/helper/mobile_test.go @@ -53,3 +53,12 @@ func TestMobileSignedMessageDecryption(t *testing.T) { assert.NotNil(t, err) assert.Nil(t, decrypted) } + +func TestGetJsonSHA256FingerprintsV4(t *testing.T) { + sha256Fingerprints, err := GetJsonSHA256Fingerprints(readTestFile("keyring_publicKey", false)) + if err != nil { + t.Fatal("Cannot unarmor key:", err) + } + + assert.Exactly(t, []byte("[\"d9ac0b857da6d2c8be985b251a9e3db31e7a1d2d832d1f07ebe838a9edce9c24\",\"203dfba1f8442c17e59214d9cd11985bfc5cc8721bb4a71740dd5507e58a1a0d\"]"), sha256Fingerprints) +}