Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API for providing untrusted data about unverified signatures #208

Merged
merged 4 commits into from
Jan 23, 2017

Conversation

mtrmac
Copy link
Collaborator

@mtrmac mtrmac commented Jan 11, 2017

@mfojtik PTAL

This utlimately adds signature.UntrustedSignatureInformation and signature.GetUntrustedSignatureInformationWithoutVerifying; https://github.com/mtrmac/skopeo/blob/unverified-contents/cmd/skopeo/signing.go#L93 is an example user, with output like

{
    "UntrustedDockerManifestDigest": "sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6",
    "UntrustedDockerReference": "mtrmac/busybox-test:latest",
    "UntrustedCreatorID": "atomic 0.1.0-dev",
    "UntrustedTimestamp": 1472502383,
    "UntrustedShortKeyIdentifier": "5A33F660B38479DF"
}

(For a real code review, see the individual commit messages for detailed descriptions. But right now this is missing tests, so please do not merge.)

UntrustedDockerManifestDigest digest.Digest
UntrustedDockerReference string // FIXME: more precise type?
UntrustedCreatorID *string
UntrustedTimestamp *int64
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a stupid int64 or a time.Time? time.Time would be nicer to use at the cost of a tiny risk that the int64Time conversion might be vulnerable (though looking at the implementation that seems extremely unlikely, for the current implementation at least).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, there is no risk/tradeoff here; we can do the UnmarshalJSON into an int64 in the private untrustedSignature, and do the conversion to time.Time in GetUntrustedSignatureInformationWithoutVerifying. Changed like that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think this should be *time.Time so the consumers life will be easier (they don't have to convert this to a time which is what you want to display to users).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the only consumer is GetUntrustedSignatureInformationWithoutVerifying, so placement of the conversion makes no difference to the total complexity of the package.

Using the native int64 here is nice in that it explicitly corresponds to the JSON semantics, and we are more certain that we can round-trip the value and that the caller setting the value is aware about the conversion (e.g., compared to time.Time, that this records neither timezones nor sub-second resolutions).

Though, perhaps, before we start using this field in earnest and before we ship the first version of a validating parser of this fields, perhaps we should change the type to a float64 (with its sub-second resolution and a more restricted precision), which is what JSON actually stores? The int64 also has conversion issues. @runcom ?

@mfojtik
Copy link
Contributor

mfojtik commented Jan 11, 2017 via email

// There is NO REASON to expect the values to be correct, or not intentionally misleading
// (including things like “✅ Verified by $authority”)
type UntrustedSignatureInformation struct {
UntrustedDockerManifestDigest digest.Digest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strip the Untrusted prefix from the fields (the struct is enough)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type name is invisible with info, err := …; and the point of use of the structure and the point of calling the producer of the value may be pretty far apart.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is more Go idiomatic to not repeat the prefix if the struct in the functions that struct define. The callers should use this like:

untrustedInfo, err := ....
untrustedInfo.DockerManifestDigest()

WDYT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I am fairly uncomfortable with providing the unverified information because I think there is a pretty big risk either code, or, more likely, humans will trust it anyway (while I do realize that providing it is unavoidable), and naming the fields this way is my last line of defense against inadvertent mistakes. Of course nothing forces any consumer to use these names in an UI.

UntrustedDockerManifestDigest digest.Digest
UntrustedDockerReference string // FIXME: more precise type?
UntrustedCreatorID *string
UntrustedTimestamp *int64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be really *time.Time

@mtrmac mtrmac force-pushed the unverified-contents branch 2 times, most recently from f8c8ea5 to ba21734 Compare January 13, 2017 17:03
func int64Field(m map[string]interface{}, fieldName string) (int64, error) {
untyped, ok := m[fieldName]
if !ok {
return -1, jsonFormatError(fmt.Sprintf("Field %s missing", fieldName))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to return -1 here. the 0 is default for int64 when not set.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it is returning -1, not 0. Anyway, there is no “good” undefined value to return here if the caller makes a mistake and ignores err. -1 is perhaps slightly less likely to be valid than a non-negative number… but it really is anybody’s guess whether that makes it more likely to be noticed as invalid or more likely to have completely unpredicted effects. (Go’s error handling sucks, it would be nice to have something like optional checked exceptions, so that a caller can’t ignore an error by mistake without it being very visible.)

It seems to me not to be worth it to bother with named return parameters so that we can skip explicitly naming a value returned in the error case—especially because that also makes a decision, namely to use 0, only that decision is not explicitly visible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mtrmac the called should never ignore "err", doing so is a programmer error and we can't prevent all human errors :-)

I'm fine with -1, just pointing out it is something unusual to me as usually if the return value is not defined, we default to a default value of the type (which in case of int64 is '0').

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mtrmac the called should never ignore "err", doing so is a programmer error and we can't prevent all human errors :-)

True, and yet, we are in the business of engineering tools and interfaces to minimize errors anyway. (Ordinarily I wouldn’t bother, but the signature subpackage brings out my worst pedantic tendencies.)

@mfojtik
Copy link
Contributor

mfojtik commented Jan 16, 2017

LGTM

@mtrmac @runcom is there something left in this PR (that cannot be a follow up?)

@mtrmac mtrmac force-pushed the unverified-contents branch 9 times, most recently from 8d83159 to 626c43f Compare January 17, 2017 21:26
@mtrmac mtrmac changed the title RFC WIP: API for providing untrusted data about unverified signatures API for providing untrusted data about unverified signatures Jan 17, 2017
@mtrmac
Copy link
Collaborator Author

mtrmac commented Jan 17, 2017

Updated:

  • Added tests
  • Separated unrelated trivial fixes into Trivial fixes #213.
  • (Tried making the timestamp a float64, but that doesn't have enough precision to represent nanosecond timestamps, so this turned out to be a dead end.)

@mfojtik @runcom PTAL, this is now ready for review.


"github.com/mtrmac/gpgme"
"golang.org/x/crypto/openpgp"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this wont work for openshift, we will not build with gpgme :(

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not add any new uses of gpgme. Getting rid of gpgme happens in #207 not here (though the two do touch the same code, so the latter one will have to be modified. I expect the newly added UntrustedSignatureContents to remain in mechanism.go as a shared helper, called by both GPG backends.)

@mfojtik
Copy link
Contributor

mfojtik commented Jan 19, 2017

LGTM

@runcom
Copy link
Member

runcom commented Jan 19, 2017

👍

Approved with PullApprove

@mtrmac mtrmac force-pushed the unverified-contents branch from 626c43f to d8face5 Compare January 19, 2017 23:23
…ng a Signature

Instead of a privateSignature containing a Signature, and using the
privateSignature type to attach private implementatinos of
json.Marshaler and json.Unmarshaler and other private methods,
use a completely separate private untrustedSignature type.

This allows us to use scarier Untrusted… names for the members, but the
only real code change is that verifyAndExtractSignature now needs to do
a member-by-member copy instead of copying the full Signature struct.

Signed-off-by: Miloslav Trmač <[email protected]>
.Unix() ignores the time location, so .UTC() does’t really do anything.

Signed-off-by: Miloslav Trmač <[email protected]>
Instead of silently embedding values in untrustedSignature.MarshalJSON
(and having to add a marshalJSONWithvariables to work around this),
make the creator ID and timestamp explicit fields of untrustedSignature,
and MarshalJSON a simple marshaller of existing data.

The values are now filled by calling newUntrustedSignature.

Now that the fields are explicit, we can also record them by
untrustedSignature.UnmarshalJSON.

This also explicitly defines the timestamp to be an integer, instead of
allowing floating-point values, because the JSON float64 is not precise
enough for nanosecond timstamps. For now, we reject fractional values,
which will allow us to record the nanosecond part separately in the
future if it became necessary.

Signed-off-by: Miloslav Trmač <[email protected]>
Add signature.GetUntrustedSignatureInformationWithoutVerifying, which
returns the contents of a signature without doing any cryptographic
verification.

Expected uses are 1) debugging verification failures, and
(CAREFULLY!) 2) listing which signatures are stored in a registry along
with an image.  It is STRONGLY recommended to do cryptographic
verification in the latter case, and to clearly indicate untrusted
signatures in the UI.

Signed-off-by: Miloslav Trmač <[email protected]>
@mtrmac
Copy link
Collaborator Author

mtrmac commented Jan 20, 2017

Tests pass in containers/skopeo#293.

@runcom
Copy link
Member

runcom commented Jan 20, 2017

Feel free to merge

@mtrmac
Copy link
Collaborator Author

mtrmac commented Jan 23, 2017

👍

Approved with PullApprove

@mtrmac mtrmac merged commit 361643f into containers:master Jan 23, 2017
@mtrmac mtrmac deleted the unverified-contents branch January 23, 2017 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants