Add API to add contexts to detached signatures.
Using the notation data packets of signatures, we add a way to set a context to detached signatures. We also add a way to enforce that signatures have the right context during verification.
This commit is contained in:
parent
3152e50f92
commit
1ec90e34ea
10 changed files with 614 additions and 19 deletions
14
CHANGELOG.md
14
CHANGELOG.md
|
|
@ -5,13 +5,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
# Changed
|
|
||||||
|
### Added
|
||||||
|
- API for adding context to detached signatures:
|
||||||
|
```go
|
||||||
|
sig, err := keyRing.SignDetachedWithContext(message, context)
|
||||||
|
```
|
||||||
|
- API to verify the context of detached signatures:
|
||||||
|
```go
|
||||||
|
err := keyRing.VerifyDetachedWithContext(message, signature, verifyTime, verificationContext)
|
||||||
|
```
|
||||||
|
### Changed
|
||||||
- Update `github.com/ProtonMail/go-crypto` to the latest version
|
- Update `github.com/ProtonMail/go-crypto` to the latest version
|
||||||
- More strictly verify detached signatures: reject detached signatures from revoked and expired keys.
|
- More strictly verify detached signatures: reject detached signatures from revoked and expired keys.
|
||||||
- In `GetVerifiedSignatureTimestamp`, use the new `VerifyDetachedSignatureAndHash` function to get the verified signature, instead of parsing the signature packets manually to get the timestamp.
|
- In `GetVerifiedSignatureTimestamp`, use the new `VerifyDetachedSignatureAndHash` function to get the verified signature, instead of parsing the signature packets manually to get the timestamp.
|
||||||
|
|
||||||
## [2.5.2] 2022-01-25
|
## [2.5.2] 2022-01-25
|
||||||
# Changed
|
### Changed
|
||||||
- Update `github.com/ProtonMail/go-crypto` to the latest version
|
- Update `github.com/ProtonMail/go-crypto` to the latest version
|
||||||
|
|
||||||
## [2.5.1] 2022-01-24
|
## [2.5.1] 2022-01-24
|
||||||
|
|
|
||||||
3
constants/context.go
Normal file
3
constants/context.go
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
package constants
|
||||||
|
|
||||||
|
const SignatureContextName = "context@proton.ch"
|
||||||
|
|
@ -62,12 +62,26 @@ func (keyRing *KeyRing) Decrypt(
|
||||||
|
|
||||||
// SignDetached generates and returns a PGPSignature for a given PlainMessage.
|
// SignDetached generates and returns a PGPSignature for a given PlainMessage.
|
||||||
func (keyRing *KeyRing) SignDetached(message *PlainMessage) (*PGPSignature, error) {
|
func (keyRing *KeyRing) SignDetached(message *PlainMessage) (*PGPSignature, error) {
|
||||||
|
return keyRing.SignDetachedWithContext(message, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignDetachedWithContext generates and returns a PGPSignature for a given PlainMessage.
|
||||||
|
// If a context is provided, it is added to the signature as notation data
|
||||||
|
// with the name set in `constants.SignatureContextName`.
|
||||||
|
func (keyRing *KeyRing) SignDetachedWithContext(message *PlainMessage, context *SigningContext) (*PGPSignature, error) {
|
||||||
signEntity, err := keyRing.getSigningEntity()
|
signEntity, err := keyRing.getSigningEntity()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var signatureNotations []*packet.Notation
|
||||||
config := &packet.Config{DefaultHash: crypto.SHA512, Time: getTimeGenerator()}
|
if context != nil {
|
||||||
|
signatureNotations = []*packet.Notation{context.getNotation()}
|
||||||
|
}
|
||||||
|
config := &packet.Config{
|
||||||
|
DefaultHash: crypto.SHA512,
|
||||||
|
Time: getTimeGenerator(),
|
||||||
|
SignatureNotations: signatureNotations,
|
||||||
|
}
|
||||||
var outBuf bytes.Buffer
|
var outBuf bytes.Buffer
|
||||||
if message.IsBinary() {
|
if message.IsBinary() {
|
||||||
err = openpgp.DetachSign(&outBuf, signEntity, message.NewReader(), config)
|
err = openpgp.DetachSign(&outBuf, signEntity, message.NewReader(), config)
|
||||||
|
|
@ -89,6 +103,22 @@ func (keyRing *KeyRing) VerifyDetached(message *PlainMessage, signature *PGPSign
|
||||||
message.NewReader(),
|
message.NewReader(),
|
||||||
signature.GetBinary(),
|
signature.GetBinary(),
|
||||||
verifyTime,
|
verifyTime,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyDetachedWithContext verifies a PlainMessage with a detached PGPSignature
|
||||||
|
// and returns a SignatureVerificationError if fails.
|
||||||
|
// If a context is provided, it verifies that the signature is valid in the given context, using
|
||||||
|
// the signature notation with name the name set in `constants.SignatureContextName`.
|
||||||
|
func (keyRing *KeyRing) VerifyDetachedWithContext(message *PlainMessage, signature *PGPSignature, verifyTime int64, verificationContext *VerificationContext) error {
|
||||||
|
_, err := verifySignature(
|
||||||
|
keyRing.entities,
|
||||||
|
message.NewReader(),
|
||||||
|
signature.GetBinary(),
|
||||||
|
verifyTime,
|
||||||
|
verificationContext,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -132,6 +162,31 @@ func (keyRing *KeyRing) GetVerifiedSignatureTimestamp(message *PlainMessage, sig
|
||||||
message.NewReader(),
|
message.NewReader(),
|
||||||
signature.GetBinary(),
|
signature.GetBinary(),
|
||||||
verifyTime,
|
verifyTime,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return sigPacket.CreationTime.Unix(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVerifiedSignatureTimestampWithContext verifies a PlainMessage with a detached PGPSignature
|
||||||
|
// returns the creation time of the signature if it succeeds
|
||||||
|
// and returns a SignatureVerificationError if fails.
|
||||||
|
// If a context is provided, it verifies that the signature is valid in the given context, using
|
||||||
|
// the signature notation with name the name set in `constants.SignatureContextName`.
|
||||||
|
func (keyRing *KeyRing) GetVerifiedSignatureTimestampWithContext(
|
||||||
|
message *PlainMessage,
|
||||||
|
signature *PGPSignature,
|
||||||
|
verifyTime int64,
|
||||||
|
verificationContext *VerificationContext,
|
||||||
|
) (int64, error) {
|
||||||
|
sigPacket, err := verifySignature(
|
||||||
|
keyRing.entities,
|
||||||
|
message.NewReader(),
|
||||||
|
signature.GetBinary(),
|
||||||
|
verifyTime,
|
||||||
|
verificationContext,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
||||||
|
|
@ -329,6 +329,27 @@ func (keyRing *KeyRing) VerifyDetachedStream(
|
||||||
message,
|
message,
|
||||||
signature.GetBinary(),
|
signature.GetBinary(),
|
||||||
verifyTime,
|
verifyTime,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyDetachedStreamWithContext verifies a message reader with a detached PGPSignature
|
||||||
|
// and returns a SignatureVerificationError if fails.
|
||||||
|
// If a context is provided, it verifies that the signature is valid in the given context, using
|
||||||
|
// the signature notations.
|
||||||
|
func (keyRing *KeyRing) VerifyDetachedStreamWithContext(
|
||||||
|
message Reader,
|
||||||
|
signature *PGPSignature,
|
||||||
|
verifyTime int64,
|
||||||
|
verificationContext *VerificationContext,
|
||||||
|
) error {
|
||||||
|
_, err := verifySignature(
|
||||||
|
keyRing.entities,
|
||||||
|
message,
|
||||||
|
signature.GetBinary(),
|
||||||
|
verifyTime,
|
||||||
|
verificationContext,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,96 @@ func verifyDetailsSignature(md *openpgp.MessageDetails, verifierKey *KeyRing) er
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SigningContext gives the context that will be
|
||||||
|
// included in the signature's notation data.
|
||||||
|
type SigningContext struct {
|
||||||
|
Value string
|
||||||
|
IsCritical bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSigningContext creates a new signing context.
|
||||||
|
// The value is set to the notation data.
|
||||||
|
// isCritical controls whether the notation is flagged as a critical packet.
|
||||||
|
func NewSigningContext(value string, isCritical bool) *SigningContext {
|
||||||
|
return &SigningContext{Value: value, IsCritical: isCritical}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (context *SigningContext) getNotation() *packet.Notation {
|
||||||
|
return &packet.Notation{
|
||||||
|
Name: constants.SignatureContextName,
|
||||||
|
Value: []byte(context.Value),
|
||||||
|
IsCritical: context.IsCritical,
|
||||||
|
IsHumanReadable: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerificationContext gives the context that will be
|
||||||
|
// used to verify the signature.
|
||||||
|
type VerificationContext struct {
|
||||||
|
Value string
|
||||||
|
IsRequired bool
|
||||||
|
RequiredAfter int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewVerificationContext creates a new verification context.
|
||||||
|
// The value is checked against the signature's notation data.
|
||||||
|
// If isRequired is false, the signature is allowed to have no context set.
|
||||||
|
// If requiredAfter is != 0, the signature is allowed to have no context set if it
|
||||||
|
// was created before the unix time set in requiredAfter.
|
||||||
|
func NewVerificationContext(value string, isRequired bool, requiredAfter int64) *VerificationContext {
|
||||||
|
return &VerificationContext{
|
||||||
|
Value: value,
|
||||||
|
IsRequired: isRequired,
|
||||||
|
RequiredAfter: requiredAfter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (context *VerificationContext) isRequiredAtTime(signatureTime time.Time) bool {
|
||||||
|
return context.IsRequired &&
|
||||||
|
(context.RequiredAfter == 0 || signatureTime.After(time.Unix(context.RequiredAfter, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func findContext(notations []*packet.Notation) (string, error) {
|
||||||
|
context := ""
|
||||||
|
for _, notation := range notations {
|
||||||
|
if notation.Name == constants.SignatureContextName {
|
||||||
|
if context != "" {
|
||||||
|
return "", errors.New("gopenpgp: signature has multiple context notations")
|
||||||
|
}
|
||||||
|
if !notation.IsHumanReadable {
|
||||||
|
return "", errors.New("gopenpgp: context notation was not set as human-readable")
|
||||||
|
}
|
||||||
|
context = string(notation.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return context, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (context *VerificationContext) verifyContext(sig *packet.Signature) error {
|
||||||
|
signatureContext, err := findContext(sig.Notations)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if signatureContext != context.Value {
|
||||||
|
contextRequired := context.isRequiredAtTime(sig.CreationTime)
|
||||||
|
if contextRequired {
|
||||||
|
return errors.New("gopenpgp: signature did not have the required context")
|
||||||
|
} else if signatureContext != "" {
|
||||||
|
return errors.New("gopenpgp: signature had a wrong context")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// verifySignature verifies if a signature is valid with the entity list.
|
// verifySignature verifies if a signature is valid with the entity list.
|
||||||
func verifySignature(pubKeyEntries openpgp.EntityList, origText io.Reader, signature []byte, verifyTime int64) (*packet.Signature, error) {
|
func verifySignature(
|
||||||
|
pubKeyEntries openpgp.EntityList,
|
||||||
|
origText io.Reader,
|
||||||
|
signature []byte,
|
||||||
|
verifyTime int64,
|
||||||
|
verificationContext *VerificationContext,
|
||||||
|
) (*packet.Signature, error) {
|
||||||
config := &packet.Config{}
|
config := &packet.Config{}
|
||||||
if verifyTime == 0 {
|
if verifyTime == 0 {
|
||||||
config.Time = func() time.Time {
|
config.Time = func() time.Time {
|
||||||
|
|
@ -130,32 +218,43 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText io.Reader, signa
|
||||||
return time.Unix(verifyTime+internal.CreationTimeOffset, 0)
|
return time.Unix(verifyTime+internal.CreationTimeOffset, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if verificationContext != nil {
|
||||||
|
config.KnownNotations = map[string]bool{constants.SignatureContextName: true}
|
||||||
|
}
|
||||||
signatureReader := bytes.NewReader(signature)
|
signatureReader := bytes.NewReader(signature)
|
||||||
|
|
||||||
sig, signer, err := openpgp.VerifyDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config)
|
sig, signer, err := openpgp.VerifyDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config)
|
||||||
|
|
||||||
if sig != nil && signer != nil && (errors.Is(err, pgpErrors.ErrSignatureExpired) || errors.Is(err, pgpErrors.ErrKeyExpired)) {
|
if sig != nil && signer != nil && (errors.Is(err, pgpErrors.ErrSignatureExpired) || errors.Is(err, pgpErrors.ErrKeyExpired)) {
|
||||||
if verifyTime == 0 { // Expiration check disabled
|
if verifyTime == 0 { // Expiration check disabled
|
||||||
return sig, nil
|
err = nil
|
||||||
}
|
} else {
|
||||||
|
// Maybe the creation time offset pushed it over the edge
|
||||||
|
// Retry with the actual verification time
|
||||||
|
config.Time = func() time.Time {
|
||||||
|
return time.Unix(verifyTime, 0)
|
||||||
|
}
|
||||||
|
|
||||||
// Maybe the creation time offset pushed it over the edge
|
_, err = signatureReader.Seek(0, io.SeekStart)
|
||||||
// Retry with the actual verification time
|
if err != nil {
|
||||||
config.Time = func() time.Time {
|
return nil, newSignatureFailed()
|
||||||
return time.Unix(verifyTime, 0)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_, err = signatureReader.Seek(0, io.SeekStart)
|
sig, signer, err = openpgp.VerifyDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config)
|
||||||
if err != nil {
|
|
||||||
return nil, newSignatureFailed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sig, signer, err = openpgp.VerifyDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil || sig == nil || signer == nil {
|
if err != nil || sig == nil || signer == nil {
|
||||||
return nil, newSignatureFailed()
|
return nil, newSignatureFailed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if verificationContext != nil {
|
||||||
|
err := verificationContext.verifyContext(sig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newSignatureFailed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sig, nil
|
return sig, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const testMessage = "Hello world!"
|
||||||
|
|
||||||
const signedPlainText = "Signed message\n"
|
const signedPlainText = "Signed message\n"
|
||||||
|
|
||||||
var textSignature, binSignature *PGPSignature
|
var textSignature, binSignature *PGPSignature
|
||||||
|
|
@ -119,7 +121,7 @@ func TestVerifyBinDetachedSig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_KeyRing_GetVerifiedSignatureTimestampSuccess(t *testing.T) {
|
func Test_KeyRing_GetVerifiedSignatureTimestampSuccess(t *testing.T) {
|
||||||
message := NewPlainMessageFromString("Hello world!")
|
message := NewPlainMessageFromString(testMessage)
|
||||||
var time int64 = 1600000000
|
var time int64 = 1600000000
|
||||||
pgp.latestServerTime = time
|
pgp.latestServerTime = time
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -218,7 +220,7 @@ func getTimestampOfIssuer(signature *PGPSignature, keyID uint64) int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_KeyRing_GetVerifiedSignatureTimestampError(t *testing.T) {
|
func Test_KeyRing_GetVerifiedSignatureTimestampError(t *testing.T) {
|
||||||
message := NewPlainMessageFromString("Hello world!")
|
message := NewPlainMessageFromString(testMessage)
|
||||||
var time int64 = 1600000000
|
var time int64 = 1600000000
|
||||||
pgp.latestServerTime = time
|
pgp.latestServerTime = time
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -234,3 +236,352 @@ func Test_KeyRing_GetVerifiedSignatureTimestampError(t *testing.T) {
|
||||||
t.Errorf("Expected an error while parsing the creation time of a wrong signature, got nil")
|
t.Errorf("Expected an error while parsing the creation time of a wrong signature, got nil")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_SignDetachedWithNonCriticalContext(t *testing.T) {
|
||||||
|
// given
|
||||||
|
|
||||||
|
context := NewSigningContext(
|
||||||
|
"test-context",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
// when
|
||||||
|
signature, err := keyRingTestPrivate.SignDetachedWithContext(
|
||||||
|
NewPlainMessage([]byte(testMessage)),
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
// then
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
p, err := packet.Read(bytes.NewReader(signature.Data))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sig, ok := p.(*packet.Signature)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Packet was not a signature")
|
||||||
|
}
|
||||||
|
notations := sig.Notations
|
||||||
|
if len(notations) != 1 {
|
||||||
|
t.Fatal("Wrong number of notations")
|
||||||
|
}
|
||||||
|
notation := notations[0]
|
||||||
|
if notation.Name != constants.SignatureContextName {
|
||||||
|
t.Fatalf("Expected notation name to be %s, got %s", constants.SignatureContextName, notation.Name)
|
||||||
|
}
|
||||||
|
if string(notation.Value) != context.Value {
|
||||||
|
t.Fatalf("Expected notation value to be %s, got %s", context.Value, notation.Value)
|
||||||
|
}
|
||||||
|
if notation.IsCritical {
|
||||||
|
t.Fatal("Expected notation to be non critical")
|
||||||
|
}
|
||||||
|
if !notation.IsHumanReadable {
|
||||||
|
t.Fatal("Expected notation to be human readable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_SignDetachedWithCriticalContext(t *testing.T) {
|
||||||
|
// given
|
||||||
|
|
||||||
|
context := NewSigningContext(
|
||||||
|
"test-context",
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
// when
|
||||||
|
signature, err := keyRingTestPrivate.SignDetachedWithContext(
|
||||||
|
NewPlainMessage([]byte(testMessage)),
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
// then
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
p, err := packet.Read(bytes.NewReader(signature.Data))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sig, ok := p.(*packet.Signature)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Packet was not a signature")
|
||||||
|
}
|
||||||
|
notations := sig.Notations
|
||||||
|
if len(notations) != 1 {
|
||||||
|
t.Fatal("Wrong number of notations")
|
||||||
|
}
|
||||||
|
notation := notations[0]
|
||||||
|
if notation.Name != constants.SignatureContextName {
|
||||||
|
t.Fatalf("Expected notation name to be %s, got %s", constants.SignatureContextName, notation.Name)
|
||||||
|
}
|
||||||
|
if string(notation.Value) != context.Value {
|
||||||
|
t.Fatalf("Expected notation value to be %s, got %s", context.Value, notation.Value)
|
||||||
|
}
|
||||||
|
if !notation.IsCritical {
|
||||||
|
t.Fatal("Expected notation to be critical")
|
||||||
|
}
|
||||||
|
if !notation.IsHumanReadable {
|
||||||
|
t.Fatal("Expected notation to be human readable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_VerifyDetachedWithUnknownCriticalContext(t *testing.T) {
|
||||||
|
// given
|
||||||
|
|
||||||
|
signatureArmored, err := ioutil.ReadFile("testdata/signature/critical_context_detached_sig")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sig, err := NewPGPSignatureFromArmored(string(signatureArmored))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
err = keyRingTestPublic.VerifyDetached(
|
||||||
|
NewPlainMessage([]byte(testMessage)),
|
||||||
|
sig,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
// then
|
||||||
|
if err == nil || !errors.Is(err, newSignatureFailed()) {
|
||||||
|
t.Fatalf("Expected a verification error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_VerifyDetachedWithUnKnownNonCriticalContext(t *testing.T) {
|
||||||
|
// given
|
||||||
|
|
||||||
|
signatureArmored, err := ioutil.ReadFile("testdata/signature/non_critical_context_detached_sig")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sig, err := NewPGPSignatureFromArmored(string(signatureArmored))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// when
|
||||||
|
err = keyRingTestPublic.VerifyDetached(
|
||||||
|
NewPlainMessage([]byte(testMessage)),
|
||||||
|
sig,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
// then
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no verification error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_VerifyDetachedWithKnownCriticalContext(t *testing.T) {
|
||||||
|
// given
|
||||||
|
|
||||||
|
signatureArmored, err := ioutil.ReadFile("testdata/signature/critical_context_detached_sig")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sig, err := NewPGPSignatureFromArmored(string(signatureArmored))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
verificationContext := NewVerificationContext(
|
||||||
|
"test-context",
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
// when
|
||||||
|
err = keyRingTestPublic.VerifyDetachedWithContext(
|
||||||
|
NewPlainMessage([]byte(testMessage)),
|
||||||
|
sig,
|
||||||
|
0,
|
||||||
|
verificationContext,
|
||||||
|
)
|
||||||
|
// then
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no verification error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_VerifyDetachedWithWrongContext(t *testing.T) {
|
||||||
|
// given
|
||||||
|
|
||||||
|
signatureArmored, err := ioutil.ReadFile("testdata/signature/critical_context_detached_sig")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sig, err := NewPGPSignatureFromArmored(string(signatureArmored))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
verificationContext := NewVerificationContext(
|
||||||
|
"another-test-context",
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
// when
|
||||||
|
err = keyRingTestPublic.VerifyDetachedWithContext(
|
||||||
|
NewPlainMessage([]byte(testMessage)),
|
||||||
|
sig,
|
||||||
|
0,
|
||||||
|
verificationContext,
|
||||||
|
)
|
||||||
|
// then
|
||||||
|
if err == nil || !errors.Is(err, newSignatureFailed()) {
|
||||||
|
t.Fatalf("Expected a verification error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_VerifyDetachedWithMissingNonRequiredContext(t *testing.T) {
|
||||||
|
// given
|
||||||
|
|
||||||
|
signatureArmored, err := ioutil.ReadFile("testdata/signature/no_context_detached_sig")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sig, err := NewPGPSignatureFromArmored(string(signatureArmored))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
verificationContext := NewVerificationContext(
|
||||||
|
"test-context",
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
// when
|
||||||
|
err = keyRingTestPublic.VerifyDetachedWithContext(
|
||||||
|
NewPlainMessage([]byte(testMessage)),
|
||||||
|
sig,
|
||||||
|
0,
|
||||||
|
verificationContext,
|
||||||
|
)
|
||||||
|
// then
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no verification error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_VerifyDetachedWithMissingRequiredContext(t *testing.T) {
|
||||||
|
// given
|
||||||
|
|
||||||
|
signatureArmored, err := ioutil.ReadFile("testdata/signature/no_context_detached_sig")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sig, err := NewPGPSignatureFromArmored(string(signatureArmored))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
verificationContext := NewVerificationContext(
|
||||||
|
"test-context",
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
// when
|
||||||
|
err = keyRingTestPublic.VerifyDetachedWithContext(
|
||||||
|
NewPlainMessage([]byte(testMessage)),
|
||||||
|
sig,
|
||||||
|
0,
|
||||||
|
verificationContext,
|
||||||
|
)
|
||||||
|
// then
|
||||||
|
if err == nil || !errors.Is(err, newSignatureFailed()) {
|
||||||
|
t.Fatalf("Expected a verification error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_VerifyDetachedWithMissingRequiredContextBeforeCutoff(t *testing.T) {
|
||||||
|
// given
|
||||||
|
signatureArmored, err := ioutil.ReadFile("testdata/signature/no_context_detached_sig")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sig, err := NewPGPSignatureFromArmored(string(signatureArmored))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
p, err := packet.Read(bytes.NewReader(sig.Data))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sigPacket, ok := p.(*packet.Signature)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Packet was not a signature")
|
||||||
|
}
|
||||||
|
verificationContext := NewVerificationContext(
|
||||||
|
"test-context",
|
||||||
|
true,
|
||||||
|
sigPacket.CreationTime.Unix()+10000,
|
||||||
|
)
|
||||||
|
// when
|
||||||
|
err = keyRingTestPublic.VerifyDetachedWithContext(
|
||||||
|
NewPlainMessage([]byte(testMessage)),
|
||||||
|
sig,
|
||||||
|
0,
|
||||||
|
verificationContext,
|
||||||
|
)
|
||||||
|
// then
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no verification error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_VerifyDetachedWithMissingRequiredContextAfterCutoff(t *testing.T) {
|
||||||
|
// given
|
||||||
|
signatureArmored, err := ioutil.ReadFile("testdata/signature/no_context_detached_sig")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sig, err := NewPGPSignatureFromArmored(string(signatureArmored))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
p, err := packet.Read(bytes.NewReader(sig.Data))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sigPacket, ok := p.(*packet.Signature)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Packet was not a signature")
|
||||||
|
}
|
||||||
|
verificationContext := NewVerificationContext(
|
||||||
|
"test-context",
|
||||||
|
true,
|
||||||
|
sigPacket.CreationTime.Unix()-10000,
|
||||||
|
)
|
||||||
|
// when
|
||||||
|
err = keyRingTestPublic.VerifyDetachedWithContext(
|
||||||
|
NewPlainMessage([]byte(testMessage)),
|
||||||
|
sig,
|
||||||
|
0,
|
||||||
|
verificationContext,
|
||||||
|
)
|
||||||
|
// then
|
||||||
|
if err == nil || !errors.Is(err, newSignatureFailed()) {
|
||||||
|
t.Fatalf("Expected a verification error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_VerifyDetachedWithDoubleContext(t *testing.T) {
|
||||||
|
// given
|
||||||
|
signatureArmored, err := ioutil.ReadFile("testdata/signature/double_critical_context_detached_sig")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sig, err := NewPGPSignatureFromArmored(string(signatureArmored))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
verificationContext := NewVerificationContext(
|
||||||
|
"test-context",
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
// when
|
||||||
|
err = keyRingTestPublic.VerifyDetachedWithContext(
|
||||||
|
NewPlainMessage([]byte(testMessage)),
|
||||||
|
sig,
|
||||||
|
0,
|
||||||
|
verificationContext,
|
||||||
|
)
|
||||||
|
// then
|
||||||
|
if err == nil || !errors.Is(err, newSignatureFailed()) {
|
||||||
|
t.Fatalf("Expected a verification error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
14
crypto/testdata/signature/critical_context_detached_sig
vendored
Normal file
14
crypto/testdata/signature/critical_context_detached_sig
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
Version: GopenPGP 2.5.2
|
||||||
|
Comment: https://gopenpgp.org
|
||||||
|
|
||||||
|
wsCaBAABCgBOBQJkBdTjCZA+tiWe3yHfJBYhBG6LoimwzMr2li+XlT62JZ7fId8k
|
||||||
|
JpSAAAAAABEADGNvbnRleHRAcHJvdG9uLmNodGVzdC1jb250ZXh0AACmMwgAmhVy
|
||||||
|
MIOgqeidOgNUQrOren3m53sA48dO0xmSRMd1HZa4uv5gDDisl+j98l7iawpvnQ1m
|
||||||
|
GqMvvrxyCV66h3W1efjGCW8lbGMKjaSZL4iUteRrAYCfsBq2l7yMDqFn+Kqns9f5
|
||||||
|
c29eh5mSxiGtmJsGSoJVFw7ZfDS+QpIw1yEsdYcyKLqdxmFS5pNQwY8uGuCrPaya
|
||||||
|
4iHLP52kGRt9pTSQTf8flwjb1bjTTJ/dOd3C2AVXtH7NmOgtLeLuc2bT6WKJFPwd
|
||||||
|
BYgCnD0r/6bcRqzqdhcV2lK3WtG1AitH0kKweXhPbtv9OGD36//04zGAeZY7BK8+
|
||||||
|
4J2lzLNX+pYtHPbnRw==
|
||||||
|
=XIJE
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
15
crypto/testdata/signature/double_critical_context_detached_sig
vendored
Normal file
15
crypto/testdata/signature/double_critical_context_detached_sig
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
Version: GopenPGP 2.5.2
|
||||||
|
Comment: https://gopenpgp.org
|
||||||
|
|
||||||
|
wsDDBAABCgB3BQJkBd5+CZA+tiWe3yHfJBYhBG6LoimwzMr2li+XlT62JZ7fId8k
|
||||||
|
JpSAAAAAABEADGNvbnRleHRAcHJvdG9uLmNodGVzdC1jb250ZXh0KJSAAAAAABEA
|
||||||
|
DmNvbnRleHRAcHJvdG9uLmNodGVzdC1jb250ZXh0LTIAAGnMB/9Z9Bd0z9Q6gvBB
|
||||||
|
xxh2v/p9PleBwytUbUMaPrL4gzRsfsKF/9kShY9ZCYpzFeTVtTHGG2C8rEiCPLev
|
||||||
|
01xr3wxVq5N8iyyF9H839qwsAKomkNrqpuAtHHF76uE/vpnqRLQ+2eCiTyOh/BSH
|
||||||
|
syizwNBRaYeVtabZVXGW5ofWFoq/sgmO4Pr63hPiTmhFbIWDOZVadN1rHOVaLBPW
|
||||||
|
mlcxb7vK2FUdcyIpwsQMH9ReDNe2FiCLy/lTWyKFYO43/6VnzHtd5Gn1MXLm2tuN
|
||||||
|
zMTEGii2WFPH8u0e6sQCcmkhJS44J8y2MFv6BKrZKcRpZfzOLPzktBvFg7Q9Pvo3
|
||||||
|
1qxluPe5
|
||||||
|
=KiYX
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
13
crypto/testdata/signature/no_context_detached_sig
vendored
Normal file
13
crypto/testdata/signature/no_context_detached_sig
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
Version: GopenPGP 2.5.2
|
||||||
|
Comment: https://gopenpgp.org
|
||||||
|
|
||||||
|
wsBzBAABCgAnBQJkBdkOCZA+tiWe3yHfJBYhBG6LoimwzMr2li+XlT62JZ7fId8k
|
||||||
|
AAAwOgf/U+wgABHyfI6Bd/1xPdUyy3FTaEY+Nj8NYi/PKez66OmLubgMEj0DfD7M
|
||||||
|
2P4SL3ZR0Y9iEtCKpncvLtlvA0sss0SZMaXH0bpJZS62cc98gLBuhE9mP1aWUu1u
|
||||||
|
+1AKVIvJKzhJC+MjKrVwMO03JrEb97ZDJylqoF2UvTeQomIY6qo5l4khDeZRVgsn
|
||||||
|
wqmq7+FLGHG75bhrW4dSOCKrNdKwodml/3l4/R8OPhRL6882egXfBtF0i0yhnX2s
|
||||||
|
4watN2OKQE8b9gfkrDWp0vA/hLLXx8IdIiuAkj55Dj6ciVXy6fTfKqcK4/IIX4MO
|
||||||
|
y5KD4MLQbmTja5KoK82mavsbhwXM7A==
|
||||||
|
=k4Xq
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
14
crypto/testdata/signature/non_critical_context_detached_sig
vendored
Normal file
14
crypto/testdata/signature/non_critical_context_detached_sig
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
Version: GopenPGP 2.5.2
|
||||||
|
Comment: https://gopenpgp.org
|
||||||
|
|
||||||
|
wsCaBAABCgBOBQJkBdcDCZA+tiWe3yHfJBYhBG6LoimwzMr2li+XlT62JZ7fId8k
|
||||||
|
JhSAAAAAABEADGNvbnRleHRAcHJvdG9uLmNodGVzdC1jb250ZXh0AAAWswgAmtfD
|
||||||
|
vf7yNlc2umZ4p8ddlcQGhkpwQgiTuaYIeJytAytPtzzSAuMUcACeBCXCTt9iXaak
|
||||||
|
ImnZULdBW6T5n/o5zVTVO5yGniOeswpXqERnp+Qmsowjd5fU+XRBnkx0cSVIrVo5
|
||||||
|
tB4gf5nxAnojusQekELnNINd8nXrWYHiDFM+aos+pTxqzWlcJv32LtQ4yuxWSzIL
|
||||||
|
9dJMIpqL+1jk2QI6E+6iTM6NkwNhYjJ7emMGJXyzPmXj4pmpJ1lYo50uHRlwirnI
|
||||||
|
VXcOkUKUwGdibnCjUv+XFoG7Qv2ilDuk/TxTKSjW7ajGjv6KAOde/pOtmpiwcWKi
|
||||||
|
OzIkiswXw5vOtLkrew==
|
||||||
|
=Ub8I
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
Loading…
Add table
Add a link
Reference in a new issue