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
|
|
@ -118,8 +118,96 @@ func verifyDetailsSignature(md *openpgp.MessageDetails, verifierKey *KeyRing) er
|
|||
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.
|
||||
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{}
|
||||
if verifyTime == 0 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if verificationContext != nil {
|
||||
config.KnownNotations = map[string]bool{constants.SignatureContextName: true}
|
||||
}
|
||||
signatureReader := bytes.NewReader(signature)
|
||||
|
||||
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 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
|
||||
// Retry with the actual verification time
|
||||
config.Time = func() time.Time {
|
||||
return time.Unix(verifyTime, 0)
|
||||
}
|
||||
_, err = signatureReader.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
return nil, newSignatureFailed()
|
||||
}
|
||||
|
||||
_, err = signatureReader.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
return nil, newSignatureFailed()
|
||||
sig, signer, err = openpgp.VerifyDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config)
|
||||
}
|
||||
|
||||
sig, signer, err = openpgp.VerifyDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config)
|
||||
}
|
||||
|
||||
if err != nil || sig == nil || signer == nil {
|
||||
return nil, newSignatureFailed()
|
||||
}
|
||||
|
||||
if verificationContext != nil {
|
||||
err := verificationContext.verifyContext(sig)
|
||||
if err != nil {
|
||||
return nil, newSignatureFailed()
|
||||
}
|
||||
}
|
||||
|
||||
return sig, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue