Add keyRing.GetVerifiedSignatureTimestamp()
Add a function to verify a detached signature and access its creation time.
This commit is contained in:
parent
eec288520e
commit
6f86adc432
7 changed files with 211 additions and 8 deletions
|
|
@ -118,6 +118,44 @@ func (keyRing *KeyRing) VerifyDetachedEncrypted(message *PlainMessage, encrypted
|
|||
return keyRing.VerifyDetached(message, signature, verifyTime)
|
||||
}
|
||||
|
||||
// GetVerifiedSignatureTimestamp verifies a PlainMessage with a detached PGPSignature
|
||||
// returns the creation time of the signature if it succeeds
|
||||
// and returns a SignatureVerificationError if fails.
|
||||
func (keyRing *KeyRing) GetVerifiedSignatureTimestamp(message *PlainMessage, signature *PGPSignature, verifyTime int64) (int64, error) {
|
||||
packets := packet.NewReader(bytes.NewReader(signature.Data))
|
||||
var err error
|
||||
var p packet.Packet
|
||||
for {
|
||||
p, err = packets.Next()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
sigPacket, ok := p.(*packet.Signature)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
var outBuf bytes.Buffer
|
||||
err = sigPacket.Serialize(&outBuf)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
err = verifySignature(
|
||||
keyRing.entities,
|
||||
message.NewReader(),
|
||||
outBuf.Bytes(),
|
||||
verifyTime,
|
||||
)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return sigPacket.CreationTime.Unix(), nil
|
||||
}
|
||||
return 0, errors.Wrap(err, "gopenpgp: can't verify any signature packets")
|
||||
}
|
||||
|
||||
// ------ INTERNAL FUNCTIONS -------
|
||||
|
||||
// Core for encryption+signature (non-streaming) functions.
|
||||
|
|
|
|||
|
|
@ -423,23 +423,23 @@ func (msg *PGPMessage) SeparateKeyAndData(estimatedLength, garbageCollector int)
|
|||
}
|
||||
|
||||
// GetBinary returns the unarmored binary content of the signature as a []byte.
|
||||
func (msg *PGPSignature) GetBinary() []byte {
|
||||
return msg.Data
|
||||
func (sig *PGPSignature) GetBinary() []byte {
|
||||
return sig.Data
|
||||
}
|
||||
|
||||
// GetArmored returns the armored signature as a string.
|
||||
func (msg *PGPSignature) GetArmored() (string, error) {
|
||||
return armor.ArmorWithType(msg.Data, constants.PGPSignatureHeader)
|
||||
func (sig *PGPSignature) GetArmored() (string, error) {
|
||||
return armor.ArmorWithType(sig.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)
|
||||
func (sig *PGPSignature) GetSignatureKeyIDs() ([]uint64, bool) {
|
||||
return getSignatureKeyIDs(sig.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())
|
||||
func (sig *PGPSignature) GetHexSignatureKeyIDs() ([]string, bool) {
|
||||
return getHexKeyIDs(sig.GetSignatureKeyIDs())
|
||||
}
|
||||
|
||||
// GetBinary returns the unarmored signed data as a []byte.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
"github.com/ProtonMail/gopenpgp/v2/constants"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
@ -73,3 +77,120 @@ func TestVerifyBinDetachedSig(t *testing.T) {
|
|||
t.Fatal("Cannot verify binary signature:", verificationError)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_KeyRing_GetVerifiedSignatureTimestampSuccess(t *testing.T) {
|
||||
message := NewPlainMessageFromString("Hello world!")
|
||||
var time int64 = 1600000000
|
||||
pgp.latestServerTime = time
|
||||
defer func() {
|
||||
pgp.latestServerTime = testTime
|
||||
}()
|
||||
signature, err := keyRingTestPrivate.SignDetached(message)
|
||||
if err != nil {
|
||||
t.Errorf("Got an error while generating the signature: %v", err)
|
||||
}
|
||||
actualTime, err := keyRingTestPublic.GetVerifiedSignatureTimestamp(message, signature, 0)
|
||||
if err != nil {
|
||||
t.Errorf("Got an error while parsing the signature creation time: %v", err)
|
||||
}
|
||||
if time != actualTime {
|
||||
t.Errorf("Expected creation time to be %d, got %d", time, actualTime)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_KeyRing_GetVerifiedSignatureWithTwoKeysTimestampSuccess(t *testing.T) {
|
||||
publicKey1Armored, err := ioutil.ReadFile("testdata/signature/publicKey1")
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't read the public key file: %v", err)
|
||||
}
|
||||
publicKey1 := parseKey(t, string(publicKey1Armored))
|
||||
publicKey2Armored, err := ioutil.ReadFile("testdata/signature/publicKey2")
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't read the public key file: %v", err)
|
||||
}
|
||||
publicKey2 := parseKey(t, string(publicKey2Armored))
|
||||
message := NewPlainMessageFromString("hello world")
|
||||
signatureArmored, err := ioutil.ReadFile("testdata/signature/detachedSigSignedTwice")
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't read the signature file: %v", err)
|
||||
}
|
||||
signature, err := NewPGPSignatureFromArmored(string(signatureArmored))
|
||||
if err != nil {
|
||||
t.Errorf("Got an error while parsing the signature: %v", err)
|
||||
}
|
||||
time1 := getTimestampOfIssuer(signature, publicKey1.GetKeyID())
|
||||
time2 := getTimestampOfIssuer(signature, publicKey2.GetKeyID())
|
||||
keyRing, err := NewKeyRing(publicKey1)
|
||||
if err != nil {
|
||||
t.Errorf("Got an error while building the key ring: %v", err)
|
||||
}
|
||||
err = keyRing.AddKey(publicKey2)
|
||||
if err != nil {
|
||||
t.Errorf("Got an error while adding key 2 to the key ring: %v", err)
|
||||
}
|
||||
actualTime, err := keyRing.GetVerifiedSignatureTimestamp(message, signature, 0)
|
||||
if err != nil {
|
||||
t.Errorf("Got an error while parsing the signature creation time: %v", err)
|
||||
}
|
||||
if time1 != actualTime {
|
||||
t.Errorf("Expected creation time to be %d, got %d", time1, actualTime)
|
||||
}
|
||||
if time2 == actualTime {
|
||||
t.Errorf("Expected creation time to be different from %d", time2)
|
||||
}
|
||||
}
|
||||
|
||||
func parseKey(t *testing.T, keyArmored string) *Key {
|
||||
key, err := NewKeyFromArmored(keyArmored)
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't parse key: %v", err)
|
||||
return nil
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func getTimestampOfIssuer(signature *PGPSignature, keyID uint64) int64 {
|
||||
packets := packet.NewReader(bytes.NewReader(signature.Data))
|
||||
var err error
|
||||
var p packet.Packet
|
||||
for {
|
||||
p, err = packets.Next()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
sigPacket, ok := p.(*packet.Signature)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
var outBuf bytes.Buffer
|
||||
err = sigPacket.Serialize(&outBuf)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if *sigPacket.IssuerKeyId == keyID {
|
||||
return sigPacket.CreationTime.Unix()
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func Test_KeyRing_GetVerifiedSignatureTimestampError(t *testing.T) {
|
||||
message := NewPlainMessageFromString("Hello world!")
|
||||
var time int64 = 1600000000
|
||||
pgp.latestServerTime = time
|
||||
defer func() {
|
||||
pgp.latestServerTime = testTime
|
||||
}()
|
||||
signature, err := keyRingTestPrivate.SignDetached(message)
|
||||
if err != nil {
|
||||
t.Errorf("Got an error while generating the signature: %v", err)
|
||||
}
|
||||
message_corrupted := NewPlainMessageFromString("Ciao world!")
|
||||
_, err = keyRingTestPublic.GetVerifiedSignatureTimestamp(message_corrupted, signature, 0)
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error while parsing the creation time of a wrong signature, got nil")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
crypto/testdata/signature/detachedSigSignedTwice
vendored
Normal file
10
crypto/testdata/signature/detachedSigSignedTwice
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
wnUEARYKAAYFAmCCo8gAIQkQyQtnL+EYbekWIQTopSabUSqDUEv/FMHJC2cv
|
||||
4Rht6VeGAP4mUJl+WYN9nLE57YByTh95OmcZmwfgz5Z4R570YqTVngD/VBym
|
||||
icc7YREcxij1gC6SSAe8kgKW6oVOWzxJ8HkOSQrCdQQBFgoABgUCYIK9vQAh
|
||||
CRCGHCX3YYW5NRYhBErDPc6OYkUaNQCLhoYcJfdhhbk1W1QBAPhrkAjimO22
|
||||
jh1V2A8pRCOs53Ig/AMAFbN37BaAIEVKAP0SVMTL6zTxYJcxWNPog7Bv5lM4
|
||||
Px4G+hZ2Kia//qlgBg==
|
||||
=0aeU
|
||||
-----END PGP SIGNATURE-----
|
||||
13
crypto/testdata/signature/publicKey1
vendored
Normal file
13
crypto/testdata/signature/publicKey1
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEYIKjXxYJKwYBBAHaRw8BAQdAbmODPSLO5tOI0GxfV+x5bgiiFriCcH3t
|
||||
6lbJkS+OzKbNEHRlc3QgPHRlc3RAYS5pdD7CjAQQFgoAHQUCYIKjXwQLCQcI
|
||||
AxUICgQWAAIBAhkBAhsDAh4BACEJEMkLZy/hGG3pFiEE6KUmm1Eqg1BL/xTB
|
||||
yQtnL+EYbenOlAEAn7A7RXQJ9FUzhuiOHeKqczdslgOO5LFcng1LuSIWn1UB
|
||||
ANWHrxnH63jnFLE82mfhpRZ5FYJ1fEXA9+3v6at3ZE8IzjgEYIKjXxIKKwYB
|
||||
BAGXVQEFAQEHQA5moGr1AKlYvKI+JpyB6W640eXpQFNSiV6LBjuMteNbAwEI
|
||||
B8J2BBgWCAAJBQJggqNfAhsMACEJEMkLZy/hGG3pFiEE6KUmm1Eqg1BL/xTB
|
||||
yQtnL+EYben97QD4hf6DttxyczHGqxGbboatBZ3IufJgFm6r2xNf9d9lSAD3
|
||||
U12oHbxyYUhapbFFkSIBo7DWJqWvx3iUEPqzY6jIAA==
|
||||
=ZWrn
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
13
crypto/testdata/signature/publicKey2
vendored
Normal file
13
crypto/testdata/signature/publicKey2
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEYIKjgxYJKwYBBAHaRw8BAQdARyd9iDlrlozcTG144XFIjWozyWLz0KQv
|
||||
fL4lqIrwM8XNEHRlc3QgPHRlc3RAYi5pdD7CjAQQFgoAHQUCYIKjgwQLCQcI
|
||||
AxUICgQWAAIBAhkBAhsDAh4BACEJEIYcJfdhhbk1FiEESsM9zo5iRRo1AIuG
|
||||
hhwl92GFuTVPRAD6A6//tK5pLPa1d7mgsoqyJ9BZyTAmnzxtbIgmOU9/TDcB
|
||||
AI4cGBfCOLzRPw6L0il5Rt78TX1jz4Dlzu6YixJcJ2AFzjgEYIKjgxIKKwYB
|
||||
BAGXVQEFAQEHQMjb0Q1FWvHzj0hyOiEN5ndChBDceUqxmQ0wOYDVqq8JAwEI
|
||||
B8J4BBgWCAAJBQJggqODAhsMACEJEIYcJfdhhbk1FiEESsM9zo5iRRo1AIuG
|
||||
hhwl92GFuTXz4AEAqn4L+ayYgphejF/ZTRIseHPK+t521CT6NZKoVaHnTWQA
|
||||
/0+kMEB5d+CH3Mb54cUganYHPLj5utO2PexEJc3xARIG
|
||||
=IEm4
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
Loading…
Add table
Add a link
Reference in a new issue