Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
PIM
libkleo
Commits
c43b6902
Commit
c43b6902
authored
Apr 28, 2021
by
Ingo Klöcker
Browse files
Take groups into account when resolving signing keys
GnuPG-bug-id: 5283
parent
50bea92e
Pipeline
#60064
passed with stage
in 7 minutes and 7 seconds
Changes
3
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
autotests/keyresolvercoretest.cpp
View file @
c43b6902
...
...
@@ -816,6 +816,308 @@ private Q_SLOTS:
QCOMPARE
(
result
.
solution
.
encryptionKeys
.
value
(
"sender-mixed@example.net"
).
size
(),
2
);
}
void
test_groups_for_signing_key__openpgp_only_mode__prefers_groups_over_keys
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-mixed@example.net"
,
{
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
,
OpenPGP
);
resolver
.
setSender
(
"sender-mixed@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
OpenPGPOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
OpenPGP
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
).
primaryFingerprint
());
}
void
test_groups_for_signing_key__openpgp_only_mode__prefers_single_protocol_groups
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-mixed@example.net"
,
OpenPGP
),
testKey
(
"sender-mixed@example.net"
,
CMS
),
}),
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
),
}),
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-smime@example.net"
,
CMS
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
,
OpenPGP
);
resolver
.
setSender
(
"sender-alias@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
OpenPGPOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
OpenPGP
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
).
primaryFingerprint
());
}
void
test_groups_for_signing_key__openpgp_only_mode__takes_key_of_mixed_protocol_groups
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-mixed@example.net"
,
OpenPGP
),
testKey
(
"sender-mixed@example.net"
,
CMS
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
,
OpenPGP
);
resolver
.
setSender
(
"sender-alias@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
OpenPGPOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
OpenPGP
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-mixed@example.net"
,
OpenPGP
).
primaryFingerprint
());
}
void
test_groups_for_signing_key__smime_only_mode__prefers_groups_over_keys
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-mixed@example.net"
,
{
testKey
(
"sender-smime@example.net"
,
CMS
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
,
CMS
);
resolver
.
setSender
(
"sender-mixed@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
CMSOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
CMS
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-smime@example.net"
,
CMS
).
primaryFingerprint
());
}
void
test_groups_for_signing_key__smime_only_mode__prefers_single_protocol_groups
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-mixed@example.net"
,
OpenPGP
),
testKey
(
"sender-mixed@example.net"
,
CMS
),
}),
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
),
}),
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-smime@example.net"
,
CMS
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
,
CMS
);
resolver
.
setSender
(
"sender-alias@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
CMSOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
CMS
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-smime@example.net"
,
CMS
).
primaryFingerprint
());
}
void
test_groups_for_signing_key__smime_only_mode__takes_key_of_mixed_protocol_groups
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-mixed@example.net"
,
OpenPGP
),
testKey
(
"sender-mixed@example.net"
,
CMS
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
,
CMS
);
resolver
.
setSender
(
"sender-alias@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
CMSOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
CMS
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-mixed@example.net"
,
CMS
).
primaryFingerprint
());
}
void
test_groups_for_signing_key__single_protocol_mode__prefers_groups_over_keys
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-mixed@example.net"
,
{
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
),
testKey
(
"sender-smime@example.net"
,
CMS
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
);
resolver
.
setAllowMixedProtocols
(
false
);
resolver
.
setSender
(
"sender-mixed@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
OpenPGPOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
OpenPGP
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
).
primaryFingerprint
());
QCOMPARE
(
result
.
alternative
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
alternative
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-smime@example.net"
,
CMS
).
primaryFingerprint
());
}
void
test_groups_for_signing_key__single_protocol_mode__prefers_single_protocol_groups
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-mixed@example.net"
,
OpenPGP
),
testKey
(
"sender-mixed@example.net"
,
CMS
),
}),
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
),
}),
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-smime@example.net"
,
CMS
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
);
resolver
.
setAllowMixedProtocols
(
false
);
resolver
.
setSender
(
"sender-alias@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
OpenPGPOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
OpenPGP
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
).
primaryFingerprint
());
QCOMPARE
(
result
.
alternative
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
alternative
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-smime@example.net"
,
CMS
).
primaryFingerprint
());
}
void
test_groups_for_signing_key__mixed_mode__prefers_groups_over_keys
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-mixed@example.net"
,
{
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
),
testKey
(
"sender-smime@example.net"
,
CMS
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
);
resolver
.
setSender
(
"sender-mixed@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
OpenPGPOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
OpenPGP
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
).
primaryFingerprint
());
}
void
test_groups_for_signing_key__mixed_mode_with_smime_preferred__prefers_groups_over_keys
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-mixed@example.net"
,
{
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
),
testKey
(
"sender-smime@example.net"
,
CMS
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
);
resolver
.
setPreferredProtocol
(
CMS
);
resolver
.
setSender
(
"sender-mixed@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
CMSOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
CMS
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-smime@example.net"
,
CMS
).
primaryFingerprint
());
}
void
test_groups_for_signing_key__mixed_mode__prefers_single_protocol_groups
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-mixed@example.net"
,
OpenPGP
),
testKey
(
"sender-mixed@example.net"
,
CMS
),
}),
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
),
}),
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-smime@example.net"
,
CMS
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
);
resolver
.
setSender
(
"sender-alias@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
OpenPGPOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
OpenPGP
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
).
primaryFingerprint
());
}
void
test_groups_for_signing_key__mixed_mode_with_smime_preferred__prefers_single_protocol_groups
()
{
const
std
::
vector
<
KeyGroup
>
groups
=
{
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-mixed@example.net"
,
OpenPGP
),
testKey
(
"sender-mixed@example.net"
,
CMS
),
}),
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-openpgp@example.net"
,
OpenPGP
),
}),
createGroup
(
"sender-alias@example.net"
,
{
testKey
(
"sender-smime@example.net"
,
CMS
),
}),
};
KeyCache
::
mutableInstance
()
->
setGroups
(
groups
);
KeyResolverCore
resolver
(
/*encrypt=*/
false
,
/*sign=*/
true
);
resolver
.
setPreferredProtocol
(
CMS
);
resolver
.
setSender
(
"sender-alias@example.net"
);
const
auto
result
=
resolver
.
resolve
();
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ResolvedMask
,
KeyResolverCore
::
AllResolved
);
QCOMPARE
(
result
.
flags
&
KeyResolverCore
::
ProtocolsMask
,
KeyResolverCore
::
CMSOnly
);
QCOMPARE
(
result
.
solution
.
protocol
,
CMS
);
QCOMPARE
(
result
.
solution
.
signingKeys
.
size
(),
1
);
QCOMPARE
(
result
.
solution
.
signingKeys
[
0
].
primaryFingerprint
(),
testKey
(
"sender-smime@example.net"
,
CMS
).
primaryFingerprint
());
}
private:
Key
testKey
(
const
char
*
email
,
Protocol
protocol
=
UnknownProtocol
)
{
...
...
src/kleo/keyresolvercore.cpp
View file @
c43b6902
...
...
@@ -29,6 +29,16 @@ using namespace GpgME;
namespace
{
QDebug
operator
<<
(
QDebug
debug
,
const
GpgME
::
Key
&
key
)
{
if
(
key
.
isNull
())
{
debug
<<
"Null"
;
}
else
{
debug
<<
Formatting
::
summaryLine
(
key
);
}
return
debug
.
maybeSpace
();
}
static
inline
bool
ValidEncryptionKey
(
const
Key
&
key
)
{
if
(
key
.
isNull
()
||
key
.
isRevoked
()
||
key
.
isExpired
()
||
...
...
@@ -106,6 +116,8 @@ public:
void
resolveOverrides
();
std
::
vector
<
Key
>
resolveRecipientWithGroup
(
const
QString
&
address
,
Protocol
protocol
);
void
resolveEncryptionGroups
();
std
::
vector
<
Key
>
resolveSenderWithGroup
(
const
QString
&
address
,
Protocol
protocol
);
void
resolveSigningGroups
();
void
resolveSign
(
Protocol
proto
);
void
setSigningKeys
(
const
QStringList
&
fingerprints
);
std
::
vector
<
Key
>
resolveRecipient
(
const
QString
&
address
,
Protocol
protocol
);
...
...
@@ -292,9 +304,60 @@ void KeyResolverCore::Private::resolveOverrides()
}
}
std
::
vector
<
Key
>
KeyResolverCore
::
Private
::
resolveSenderWithGroup
(
const
QString
&
address
,
Protocol
protocol
)
{
// prefer single-protocol groups over mixed-protocol groups
auto
group
=
mCache
->
findGroup
(
address
,
protocol
,
KeyUsage
::
Sign
);
if
(
group
.
isNull
())
{
group
=
mCache
->
findGroup
(
address
,
UnknownProtocol
,
KeyUsage
::
Sign
);
}
if
(
group
.
isNull
())
{
return
{};
}
// take the first key matching the protocol
const
auto
&
keys
=
group
.
keys
();
const
auto
it
=
std
::
find_if
(
std
::
begin
(
keys
),
std
::
end
(
keys
),
[
protocol
]
(
const
auto
&
key
)
{
return
key
.
protocol
()
==
protocol
;
});
if
(
it
==
std
::
end
(
keys
))
{
qCDebug
(
LIBKLEO_LOG
)
<<
"group"
<<
group
.
name
()
<<
"has no"
<<
Formatting
::
displayName
(
protocol
)
<<
"signing key"
;
return
{};
}
const
auto
key
=
*
it
;
if
(
!
isAcceptableSigningKey
(
key
))
{
qCDebug
(
LIBKLEO_LOG
)
<<
"group"
<<
group
.
name
()
<<
"has unacceptable signing key"
<<
key
;
return
{};
}
return
{
key
};
}
void
KeyResolverCore
::
Private
::
resolveSigningGroups
()
{
auto
&
protocolKeysMap
=
mSigKeys
;
if
(
!
protocolKeysMap
[
UnknownProtocol
].
empty
())
{
// already resolved by common override
return
;
}
if
(
mFormat
==
OpenPGP
)
{
if
(
!
protocolKeysMap
[
OpenPGP
].
empty
())
{
// already resolved by override
return
;
}
protocolKeysMap
[
OpenPGP
]
=
resolveSenderWithGroup
(
mSender
,
OpenPGP
);
}
else
if
(
mFormat
==
CMS
)
{
if
(
!
protocolKeysMap
[
CMS
].
empty
())
{
// already resolved by override
return
;
}
protocolKeysMap
[
CMS
]
=
resolveSenderWithGroup
(
mSender
,
CMS
);
}
else
{
protocolKeysMap
[
OpenPGP
]
=
resolveSenderWithGroup
(
mSender
,
OpenPGP
);
protocolKeysMap
[
CMS
]
=
resolveSenderWithGroup
(
mSender
,
CMS
);
}
}
void
KeyResolverCore
::
Private
::
resolveSign
(
Protocol
proto
)
{
if
(
mSigKeys
.
contains
(
proto
))
{
if
(
!
mSigKeys
[
proto
].
empty
(
))
{
// Explicitly set
return
;
}
...
...
@@ -546,6 +609,9 @@ KeyResolverCore::Result KeyResolverCore::Private::resolve()
}
// Next look for matching groups of keys
if
(
mSign
)
{
resolveSigningGroups
();
}
if
(
mEncrypt
)
{
resolveEncryptionGroups
();
}
...
...
src/models/keycache.h
View file @
c43b6902
...
...
@@ -106,6 +106,15 @@ public:
* Looks for a group named @a name which contains keys with protocol @a protocol
* that are suitable for the usage @a usage.
*
* If @a protocol is GpgME::OpenPGP or GpgME::CMS, then only groups consisting of keys
* matching this protocol are considered. Use @a protocol GpgME::UnknownProtocol to consider
* any groups regardless of the protocol including mixed-protocol groups.
*
* If @a usage is not KeyUsage::AnyUsage, then only groups consisting of keys supporting this usage
* are considered.
* The validity of keys and the presence of a private key (necessary for signing, certification, and
* authentication) is not taken into account.
*
* The first group that fulfills all conditions is returned.
*
* @returns a matching group or a null group if no matching group is found.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment