Add KeyIDs public API functionality (#76)
* Add public KeyIDs functions * Add signature keyIDs functions * Lint code
This commit is contained in:
parent
1f4d966115
commit
2f89b9fa0e
7 changed files with 212 additions and 9 deletions
47
CHANGELOG.md
47
CHANGELOG.md
|
|
@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
(key *Key) ArmorWithCustomHeaders(comment, version string) (string, error)
|
||||
(key *Key) GetArmoredPublicKeyWithCustomHeaders(comment, version string) (string, error)
|
||||
```
|
||||
|
||||
- Message armoring with custom headers
|
||||
```go
|
||||
(msg *PGPMessage) GetArmoredWithCustomHeaders(comment, version string) (string, error)
|
||||
|
|
@ -18,7 +19,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- Extraction of encryption key IDs from a PGP message, i.e. the IDs of the keys used in the encryption of the session key
|
||||
```go
|
||||
(msg *PGPMessage) getEncryptionKeyIDs() ([]uint64, bool)
|
||||
(msg *PGPMessage) GetEncryptionKeyIDs() ([]uint64, bool)
|
||||
(msg *PGPMessage) GetHexEncryptionKeyIDs() ([]uint64, bool)
|
||||
```
|
||||
|
||||
- Extraction of signing key IDs from a PGP message, i.e. the IDs of the keys used in the signature of the message
|
||||
(of all the readable, unencrypted signature packets)
|
||||
```go
|
||||
(msg *PGPMessage) GetSignatureKeyIDs() ([]uint64, bool)
|
||||
(msg *PGPMessage) GetHexSignatureKeyIDs() ([]string, bool)
|
||||
```
|
||||
|
||||
- Getter for the x/crypto Entity (internal components of an OpenPGP key) from Key struct
|
||||
|
|
@ -37,17 +46,49 @@ DecryptBinaryMessageArmored(privateKey string, passphrase []byte, ciphertext str
|
|||
(key *Key) ToPublic() (publicKey *Key, err error)
|
||||
```
|
||||
|
||||
- Helpers to handle detached signatures
|
||||
```go
|
||||
EncryptSignArmoredDetached(
|
||||
publicKey, privateKey string,
|
||||
passphrase, plainData []byte,
|
||||
) (ciphertext, signature string, err error)
|
||||
|
||||
DecryptVerifyArmoredDetached(
|
||||
publicKey, privateKey string,
|
||||
passphrase []byte,
|
||||
ciphertext string,
|
||||
armoredSignature string,
|
||||
) (plainData []byte, err error)
|
||||
```
|
||||
|
||||
- `EncryptSignArmoredDetachedMobileResult` Struct (with its helper) to allow detached signature + encryption in one pass
|
||||
```go
|
||||
type EncryptSignArmoredDetachedMobileResult struct {
|
||||
Ciphertext, Signature string
|
||||
}
|
||||
|
||||
EncryptSignArmoredDetachedMobile(
|
||||
publicKey, privateKey string,
|
||||
passphrase, plainData []byte,
|
||||
) (wrappedTuple *EncryptSignArmoredDetachedMobileResult, err error)
|
||||
```
|
||||
|
||||
### Changed
|
||||
- Improved key and message armoring testing
|
||||
- `EncryptSessionKey` now creates encrypted key packets for each valid encryption key in the provided keyring.
|
||||
Returns a byte slice with all the concatenated key packets.
|
||||
- Use aes256 chiper for message encryption with password.
|
||||
- Use aes256 cipher for password-encrypted messages.
|
||||
- The helpers `EncryptSignMessageArmored`, `DecryptVerifyMessageArmored`, `DecryptVerifyAttachment`, and`DecryptBinaryMessageArmored`
|
||||
now accept private keys as public keys and perform automatic casting if the keys are locked.
|
||||
|
||||
### Fixed
|
||||
- Public key armoring headers
|
||||
- `EncryptSessionKey` throws an error when invalid encryption keys are provided
|
||||
- Session keys' size is now checked against the expected value to prevent panics
|
||||
|
||||
- Hex Key IDs returned from `(key *Key) GetHexKeyID() string` are now correctly padded
|
||||
- Avoid panics in `(msg *PGPMessage) GetEncryptionKeyIDs() ([]uint64, bool)` by breaking the packet.next cycle on specific packet types
|
||||
- Prevent the server time from going backwards in `UpdateTime`
|
||||
|
||||
## [2.0.1] - 2020-05-01
|
||||
### Security
|
||||
- Updated underlying crypto library
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ func (key *Key) PrintFingerprints() {
|
|||
|
||||
// GetHexKeyID returns the key ID, hex encoded as a string.
|
||||
func (key *Key) GetHexKeyID() string {
|
||||
return strconv.FormatUint(key.GetKeyID(), 16)
|
||||
return keyIDToHex(key.GetKeyID())
|
||||
}
|
||||
|
||||
// GetKeyID returns the key ID, encoded as 8-byte int.
|
||||
|
|
@ -465,3 +465,8 @@ func generateKey(
|
|||
|
||||
return &Key{newEntity}, nil
|
||||
}
|
||||
|
||||
// keyIDToHex casts a keyID to hex with the correct padding.
|
||||
func keyIDToHex(keyID uint64) string {
|
||||
return fmt.Sprintf("%016v", strconv.FormatUint(keyID, 16))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,8 +223,8 @@ func (msg *PGPMessage) GetArmoredWithCustomHeaders(comment, version string) (str
|
|||
return armor.ArmorWithTypeAndCustomHeaders(msg.Data, constants.PGPMessageHeader, version, comment)
|
||||
}
|
||||
|
||||
// getEncryptionKeyIds Returns the key IDs of the keys to which the session key is encrypted.
|
||||
func (msg *PGPMessage) getEncryptionKeyIDs() ([]uint64, bool) {
|
||||
// GetEncryptionKeyIDs Returns the key IDs of the keys to which the session key is encrypted.
|
||||
func (msg *PGPMessage) GetEncryptionKeyIDs() ([]uint64, bool) {
|
||||
packets := packet.NewReader(bytes.NewReader(msg.Data))
|
||||
var err error
|
||||
var ids []uint64
|
||||
|
|
@ -252,6 +252,21 @@ Loop:
|
|||
return ids, false
|
||||
}
|
||||
|
||||
// GetHexEncryptionKeyIDs Returns the key IDs of the keys to which the session key is encrypted.
|
||||
func (msg *PGPMessage) GetHexEncryptionKeyIDs() ([]string, bool) {
|
||||
return getHexKeyIDs(msg.GetEncryptionKeyIDs())
|
||||
}
|
||||
|
||||
// GetSignatureKeyIDs Returns the key IDs of the keys to which the (readable) signature packets are encrypted to.
|
||||
func (msg *PGPMessage) GetSignatureKeyIDs() ([]uint64, bool) {
|
||||
return getSignatureKeyIDs(msg.Data)
|
||||
}
|
||||
|
||||
// GetHexSignatureKeyIDs Returns the key IDs of the keys to which the session key is encrypted.
|
||||
func (msg *PGPMessage) GetHexSignatureKeyIDs() ([]string, bool) {
|
||||
return getHexKeyIDs(msg.GetSignatureKeyIDs())
|
||||
}
|
||||
|
||||
// GetBinaryDataPacket returns the unarmored binary datapacket as a []byte.
|
||||
func (msg *PGPSplitMessage) GetBinaryDataPacket() []byte {
|
||||
return msg.DataPacket
|
||||
|
|
@ -386,6 +401,16 @@ func (msg *PGPSignature) GetArmored() (string, error) {
|
|||
return armor.ArmorWithType(msg.Data, constants.PGPSignatureHeader)
|
||||
}
|
||||
|
||||
// GetSignatureKeyIDs Returns the key IDs of the keys to which the (readable) signature packets are encrypted to.
|
||||
func (msg *PGPSignature) GetSignatureKeyIDs() ([]uint64, bool) {
|
||||
return getSignatureKeyIDs(msg.Data)
|
||||
}
|
||||
|
||||
// GetHexSignatureKeyIDs Returns the key IDs of the keys to which the session key is encrypted.
|
||||
func (msg *PGPSignature) GetHexSignatureKeyIDs() ([]string, bool) {
|
||||
return getHexKeyIDs(msg.GetSignatureKeyIDs())
|
||||
}
|
||||
|
||||
// GetBinary returns the unarmored signed data as a []byte.
|
||||
func (msg *ClearTextMessage) GetBinary() []byte {
|
||||
return msg.Data
|
||||
|
|
@ -425,3 +450,48 @@ func IsPGPMessage(data string) bool {
|
|||
constants.PGPMessageHeader + "-----")
|
||||
return re.MatchString(data)
|
||||
}
|
||||
|
||||
func getSignatureKeyIDs(data []byte) ([]uint64, bool) {
|
||||
packets := packet.NewReader(bytes.NewReader(data))
|
||||
var err error
|
||||
var ids []uint64
|
||||
var onePassSignaturePacket *packet.OnePassSignature
|
||||
var signaturePacket *packet.Signature
|
||||
|
||||
Loop:
|
||||
for {
|
||||
var p packet.Packet
|
||||
if p, err = packets.Next(); err == io.EOF {
|
||||
break
|
||||
}
|
||||
switch p := p.(type) {
|
||||
case *packet.OnePassSignature:
|
||||
onePassSignaturePacket = p
|
||||
ids = append(ids, onePassSignaturePacket.KeyId)
|
||||
case *packet.Signature:
|
||||
signaturePacket = p
|
||||
if signaturePacket.IssuerKeyId != nil {
|
||||
ids = append(ids, *signaturePacket.IssuerKeyId)
|
||||
}
|
||||
case *packet.SymmetricallyEncrypted,
|
||||
*packet.AEADEncrypted,
|
||||
*packet.Compressed,
|
||||
*packet.LiteralData:
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
return ids, true
|
||||
}
|
||||
return ids, false
|
||||
}
|
||||
|
||||
func getHexKeyIDs(keyIDs []uint64, ok bool) ([]string, bool) {
|
||||
hexIDs := make([]string, len(keyIDs))
|
||||
|
||||
for i, id := range keyIDs {
|
||||
hexIDs[i] = keyIDToHex(id)
|
||||
}
|
||||
|
||||
return hexIDs, ok
|
||||
}
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ func TestMultipleKeyMessageEncryption(t *testing.T) {
|
|||
assert.Exactly(t, message.GetString(), decrypted.GetString())
|
||||
}
|
||||
|
||||
func TestMessagegetGetEncryptionKeyIDs(t *testing.T) {
|
||||
func TestMessageGetEncryptionKeyIDs(t *testing.T) {
|
||||
var message = NewPlainMessageFromString("plain text")
|
||||
assert.Exactly(t, 3, len(keyRingTestMultiple.entities))
|
||||
|
||||
|
|
@ -236,7 +236,7 @@ func TestMessagegetGetEncryptionKeyIDs(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal("Expected no error when encrypting, got:", err)
|
||||
}
|
||||
ids, ok := ciphertext.getEncryptionKeyIDs()
|
||||
ids, ok := ciphertext.GetEncryptionKeyIDs()
|
||||
assert.Exactly(t, 3, len(ids))
|
||||
assert.True(t, ok)
|
||||
encKey, ok := keyRingTestMultiple.entities[0].EncryptionKey(time.Now())
|
||||
|
|
@ -244,6 +244,50 @@ func TestMessagegetGetEncryptionKeyIDs(t *testing.T) {
|
|||
assert.Exactly(t, encKey.PublicKey.KeyId, ids[0])
|
||||
}
|
||||
|
||||
func TestMessageGetHexGetEncryptionKeyIDs(t *testing.T) {
|
||||
ciphertext, err := NewPGPMessageFromArmored(readTestFile("message_multipleKeyID", false))
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error when reading message, got:", err)
|
||||
}
|
||||
|
||||
ids, ok := ciphertext.GetHexEncryptionKeyIDs()
|
||||
assert.Exactly(t, 2, len(ids))
|
||||
assert.True(t, ok)
|
||||
|
||||
assert.Exactly(t, "76ad736fa7e0e83c", ids[0])
|
||||
assert.Exactly(t, "0f65b7ae456a9ceb", ids[1])
|
||||
}
|
||||
|
||||
func TestMessageGetSignatureKeyIDs(t *testing.T) {
|
||||
var message = NewPlainMessageFromString("plain text")
|
||||
|
||||
signature, err := keyRingTestPrivate.SignDetached(message)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error when encrypting, got:", err)
|
||||
}
|
||||
|
||||
ids, ok := signature.GetSignatureKeyIDs()
|
||||
assert.Exactly(t, 1, len(ids))
|
||||
assert.True(t, ok)
|
||||
signingKey, ok := keyRingTestPrivate.entities[0].SigningKey(time.Now())
|
||||
assert.True(t, ok)
|
||||
assert.Exactly(t, signingKey.PublicKey.KeyId, ids[0])
|
||||
}
|
||||
|
||||
func TestMessageGetHexSignatureKeyIDs(t *testing.T) {
|
||||
ciphertext, err := NewPGPMessageFromArmored(readTestFile("message_plainSignature", false))
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error when reading message, got:", err)
|
||||
}
|
||||
|
||||
ids, ok := ciphertext.GetHexSignatureKeyIDs()
|
||||
assert.Exactly(t, 2, len(ids))
|
||||
assert.True(t, ok)
|
||||
|
||||
assert.Exactly(t, "3eb6259edf21df24", ids[0])
|
||||
assert.Exactly(t, "d05b722681936ad0", ids[1])
|
||||
}
|
||||
|
||||
func TestMessageGetArmoredWithCustomHeaders(t *testing.T) {
|
||||
var message = NewPlainMessageFromString("plain text")
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ func TestDataPacketEncryption(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal("Unable to unarmor pgp message, got:", err)
|
||||
}
|
||||
ids, ok := pgpMessage.getEncryptionKeyIDs()
|
||||
ids, ok := pgpMessage.GetEncryptionKeyIDs()
|
||||
assert.True(t, ok)
|
||||
assert.Exactly(t, 3, len(ids))
|
||||
|
||||
|
|
|
|||
20
crypto/testdata/message_multipleKeyID
vendored
Normal file
20
crypto/testdata/message_multipleKeyID
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN PGP MESSAGE-----
|
||||
Version: OpenPGP.js v3.1.3
|
||||
Comment: https://openpgpjs.org
|
||||
|
||||
wcBMA3atc2+n4Og8AQf+PiFMi7BkL/Ppe6kILs0E/ik0jR4sAXaNA0kpJYYE
|
||||
FhWBZ8RWOFLeriyABaY2gQD86kQ7TZSgJcOoBYWLEIRdnHP0ss4niG1WuEPB
|
||||
gyKXdyyS99URdYtxCV0ErxPHObdO61vxwsE7Fx9nC3Lk8mnktzfqWQelA9Ej
|
||||
TLj9pO3M+uo2H5baZ8ylErcTRmgyQlSy+gcjCDeLs94kDpbdJlZX1x+79Zub
|
||||
2iWnOSmdmW2ZhbBq7w8az5qhPUfMMNIRcfieOAQwkKz5dBoCO81mwHcd+LWY
|
||||
EiiNDNVrD5uYV4t0PqHp9o5537JV//tlG1UEWvNhQ35tqXgsmND7MQkb2ct3
|
||||
6cHATAMPZbeuRWqc6wEH/2wKL4h9QOOxBAJKHf1MJlVbMN8qgwtKr7q/YcdH
|
||||
gooTF5asSMq91qKCcW38NuFXkdzf5sGZsOA0yLTQnjbu+42RzBfty10qTZg0
|
||||
v1zmgKErACFpsztYNOdsJh3aGJ4WjytpZWKL+PqHnX4/HR+zbEMEGfDjhTBZ
|
||||
eL8TLa3F3OK533F2oNAO5ITYsdnipVmiy89FL5yt/9ZpvFRRuj8lOwJYTe4O
|
||||
pQ+ZbenRZ0sXyEIV6xZguqvFICOELoy4LM3kHpQfY4Gi1JPT/buWxnDFugEW
|
||||
u5JQ6qix0Y1KunuWSogEjfaJ8BgLSBs/U7RxxjLYETFB15VxyEaQJx9wx3Id
|
||||
uU7SRQG+UkFbDn9ghZoF7ROPTXAUnHlqODGxdgnPhJJQaPSkNOMALkBI6I4Q
|
||||
lqsO2LprVTVeCGo+Qd3WrE5MqGunvDli5VK37A==
|
||||
=QeVN
|
||||
-----END PGP MESSAGE-----
|
||||
23
crypto/testdata/message_plainSignature
vendored
Normal file
23
crypto/testdata/message_plainSignature
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN PGP MESSAGE-----
|
||||
Comment: https://gopenpgp.org
|
||||
Version: GopenPGP 2.0.1
|
||||
|
||||
wcBMAw9lt65FapzrAQf/bAoviH1A47EEAkod/UwmVVsw3yqDC0qvur9hx0eCihMX
|
||||
lqxIyr3WooJxbfw24VeR3N/mwZmw4DTItNCeNu77jZHMF+3LXSpNmDS/XOaAoSsA
|
||||
IWmzO1g052wmHdoYnhaPK2llYov4+oedfj8dH7NsQwQZ8OOFMFl4vxMtrcXc4rnf
|
||||
cXag0A7khNix2eKlWaLLz0UvnK3/1mm8VFG6PyU7AlhN7g6lD5lt6dFnSxfIQhXr
|
||||
FmC6q8UgI4QujLgszeQelB9jgaLUk9P9u5bGcMW6ARa7klDqqLHRjUq6e5ZKiASN
|
||||
9onwGAtIGz9TtHHGMtgRMUHXlXHIRpAnH3DHch25TsLAXAQAAQoAEAUCXNlzAwkQ
|
||||
PrYlnt8h3yQAAMuZCACipXp2GmSo26JgRJADNan01cBu6nVbzpNHNqKqUNLDnCvZ
|
||||
L4HjXeUQ/o+vl8GSpy51kvcXmNsD36d4agnzDf7OjiIdcLns/ARSUESQyrprf+oF
|
||||
+OYTeRXufxCoiG35Kn82g4ML2ifj52c+E/mS7ZTupQgSZrXPcS7XNAEuAuOnjC8O
|
||||
5TpzlA3hKwirVzRVmyn2wlTVWQWWMNoci8esnLH/eZVt/3DFCBwVG3D+avsnKXN3
|
||||
CI6kAFtiRA9drDHXT56AR4lZGotTltG2g5D3exAzczuHpxA4Qshp/03dDADBAm4T
|
||||
3IJ6p/7rZ3wgmeaRfcg9sAxEJ4y1pME1ma8jBrB8wpwEAAEKABAFAlzZcwMJENBb
|
||||
ciaBk2rQAADeRAP+LCDF4zEXVQYhQXBnvVGEK1Ar3R/0lj3GSWczb3+QYbcmwAZR
|
||||
n+ll3xlrgCqls4BfVBvXQ/hyABF3HPlkFRNodHLonq+fuvjCgEnsdJG18/yzfeP6
|
||||
Ox0w2vHRE3ad78dhJvyuWqL7Wd8L2EG9MsCdzx5MQnfWShzQE4EcSnbCtprSRQG+
|
||||
UkFbDn9ghZoF7ROPTXAUnHlqODGxdgnPhJJQaPSkNOMALkBI6I4QlqsO2LprVTVe
|
||||
CGo+Qd3WrE5MqGunvDli5VK37A==
|
||||
=cPmR
|
||||
-----END PGP MESSAGE-----
|
||||
Loading…
Add table
Add a link
Reference in a new issue