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

Refactor signature object to add support for repository countersignatures #2006

Merged
merged 7 commits into from
Feb 9, 2018

Conversation

PatoBeltran
Copy link
Contributor

First part of NuGet/Home#6276

This work is being done in multiple PRs to avoid having one huge and difficult PR to review.

This PR refactors the current signature object to be able to have repository primary signatures, repository counter signatures in addition to the current author primary signatures.

@mishra14
Copy link
Contributor

mishra14 commented Feb 6, 2018

Had an offline discussion with @PatoBeltran.

We should consider the following approach -
Have the class structure as it is in the PR i.e. AuthorPrimarySignature implements Signature etc.
But in such a case, we should consider moving the verification logic to the individual classes to warrant such a finer control on the types.
For instance, Signature should be an abstract class and implement all the common verification logic in Signature.Verify() and the child classes can then override the base verify method to add extra verification and call base.Verify().

Or if we want to keep the implementation simple then -
Signature should represent the complete signature of a package.
Signature.Type will then indicate if this is a repo or an author signature. It should contain a counter signature object - Signature.CounterSignature of type Signature.
That will satisfy the requirements as well, while keeping this simpler.

@@ -364,6 +364,11 @@ public enum NuGetLogCode
/// </summary>
NU3029 = 3029,

/// <summary>
/// The package signature cointains multiple repository countersignatures.
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: contains

{
throw new NotSupportedException();
}

private Task<Signature> TimestampSignature(SignPackageRequest request, ILogger logger, Signature signature, CancellationToken token)
private Task<PrimarySignature> TimestampPrimarySignature(SignPackageRequest request, ILogger logger, PrimarySignature signature, CancellationToken token)
Copy link
Contributor

Choose a reason for hiding this comment

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

TimestampPrimarySignatureAsync

}

private Task<Signature> TimestampSignature(SignPackageRequest request, ILogger logger, Signature signature, CancellationToken token)
private Task<PrimarySignature> TimestampPrimarySignature(SignPackageRequest request, ILogger logger, PrimarySignature signature, CancellationToken token)
Copy link
Contributor

Choose a reason for hiding this comment

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

TimestampPrimarySignatureAsync


namespace NuGet.Packaging.Signing
{
public class AuthorPrimarySignature : PrimarySignature
Copy link
Contributor

Choose a reason for hiding this comment

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

sealed


namespace NuGet.Packaging.Signing
{
public class RepositoryPrimarySignature : PrimarySignature, IRepositorySignature
Copy link
Contributor

Choose a reason for hiding this comment

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

sealed

potentialNuGetV3ServiceIndexUrl = AttributeUtility.GetNuGetV3ServiceIndexUrl(counterSignature.SignedAttributes);
}
// This counter signature is not a valid repository countersignature
catch (SignatureException) { }
Copy link
Contributor

Choose a reason for hiding this comment

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

After you determine it's a repository countersignature, do not catch this exception. Let the exception bubble up to indicate it's an invalid repository countersignature.

throw new SignatureException(NuGetLogCode.NU3030, Strings.Error_NotOneRepositoryCounterSignature);
}

var respoSignature = repositoryCounterSignatures.FirstOrDefault();
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: repoCountersignature

public static RepositoryCounterSignature GetRepositoryCounterSignature(PrimarySignature primarySignature)
{
var counterSignatures = primarySignature.SignerInfo.CounterSignerInfos;
var repositoryCounterSignatures = new List<SignerInfo>();
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need a list.

Create a local:

RepositoryCounterSignature repositoryCountersignature = null;

When examining countersignatures, assign this local only if null; otherwise throw.


namespace NuGet.Packaging.Signing
{
public class UnknownPrimarySignature : PrimarySignature
Copy link
Contributor

Choose a reason for hiding this comment

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

sealed

VerifySigningTimeAttribute(SignerInfo);
}

private static void VerifySigningTimeAttribute(SignerInfo signerInfo)
Copy link
Contributor

Choose a reason for hiding this comment

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

@PatoBeltran PatoBeltran force-pushed the dev-patobeltran-signing-repo-first branch from 8334989 to 5b0840c Compare February 7, 2018 22:11
@PatoBeltran
Copy link
Contributor Author

@mishra14 @dtivel pushed some new commits, rebased from dev and addressed feedback 😄

@@ -28,7 +28,7 @@ public static class CertificateUtility
public static string X509Certificate2ToString(X509Certificate2 cert, HashAlgorithmName fingerprintAlgorithm)
{
var certStringBuilder = new StringBuilder();
X509Certificate2ToString(cert, certStringBuilder, fingerprintAlgorithm, indentation: "");
X509Certificate2ToString(cert, certStringBuilder, fingerprintAlgorithm, indentation: " ");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added indentation to all certs to match the output from signtool, see example in NuGet/Home#6005 (comment)

string.Join(", ", chainStatusList.Select(x => x.Status.ToString())))));
}
}
signatureIssues.AddRange(timestampIssues);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is to display first the details of the signature and afterwards for its timestamp (if present). This matches signtool and makes more sense to display first the signature. (example output of signtool NuGet/Home#6005 (comment))

NU3032 = 3032,

/// <summary>
/// A repository primary signature should not have a repository countersignatures.
Copy link
Contributor

Choose a reason for hiding this comment

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

A repository primary signature must not have a repository countersignature.

{
}

internal override SignatureVerificationStatus Verify(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we just pass a settings object instead of a bunch of flags? It's easier to maintain over time.

internal override SignatureVerificationStatus Verify(
Timestamp timestamp,
bool allowUntrusted,
bool allowUntrustedSelfSignedCertificate,
Copy link
Contributor

Choose a reason for hiding this comment

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

#if IS_DESKTOP
Uri NuGetV3ServiceIndexUrl { get; }

IReadOnlyList<string> NuGetPackageOwners { get; }
Copy link
Contributor

Choose a reason for hiding this comment

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

PackageOwners

public interface IRepositorySignature
{
#if IS_DESKTOP
Uri NuGetV3ServiceIndexUrl { get; }
Copy link
Contributor

Choose a reason for hiding this comment

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

V3ServiceIndexUrl

ThrowForInvalidRepositoryCounterSignature();
}

private static void ThrowForInvalidRepositoryCounterSignature()
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need this method. Just move the method body into ThrowForInvalidSignature().

X509Certificate2Collection certificateExtraStore,
List<SignatureLog> issues)
{
issues?.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.SignatureType, Type.ToString())));
Copy link
Contributor

Choose a reason for hiding this comment

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

Why allow issues to be null here and in other files?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I though it was a good idea to allow in the case you want to have the verification but don't care about logs, because even if there are no issues the same validation will be performed and you would get the same result. Do you think we should enforce the existence of issues?

@@ -29,7 +29,7 @@ public SignatureTests(CertificatesFixture fixture)
public void Load_WhenDataNull_Throws()
{
var exception = Assert.Throws<ArgumentNullException>(
() => Signature.Load(data: null));
() => PrimarySignature.Load(data: null));
Copy link
Contributor

Choose a reason for hiding this comment

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

Rename file to PrimarySignatureTests.

@@ -80,25 +80,25 @@ public void GetPrimarySignatureCertificates_WithAuthorSignature_ReturnsCertifica
public void GetPrimarySignatureTimestampSignatureCertificates_WhenSignatureNull_Throws()
{
var exception = Assert.Throws<ArgumentNullException>(
() => SignatureUtility.GetPrimarySignatureTimestampSignatureCertificates(signature: null));
() => SignatureUtility.GetPrimarySignatureTimestampCertificates(signature: null));
Copy link
Contributor

Choose a reason for hiding this comment

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

This test (and others) were testing the method GetPrimarySignatureTimestampSignatureCertificates(...). The method name begins with the name of the member under test. Since you renamed the name of the member under test, update the test name too. This feedback applies globally.

<MemberName>_<TestCondition>_<ExpectedResult>

ThrowForInvalidPrimarySignature();
}

protected static void ThrowForInvalidPrimarySignature()
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure this method is needed. Just move the body into ThrowForInvalidSignature().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I decided to have this method not as part of ThrowForInvalidSignature() because it is usefull as a static method in some of the static methods in this class, and because ThrowForInvalidSignature() is an abstract method it cannot be static. Therefore I decided to have another method that is static with the desired implementation and just let ThrowForInvalidSignature() call this method.

@PatoBeltran PatoBeltran force-pushed the dev-patobeltran-signing-repo-first branch from cb14d1f to 8105baf Compare February 9, 2018 00:40
Copy link
Contributor

@mishra14 mishra14 left a comment

Choose a reason for hiding this comment

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

Consider moving signed cms into signature.... otherwise looks good.

/// <summary>
/// A SignedCms object holding the signature and SignerInfo.
/// </summary>
public SignedCms SignedCms { get; }
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be moved into Signature, since all signatures need to atleast be a SignedCms

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Countersignatures are represented as SignerInfo, so they don't have their own SignedCms only Primary signatures will have it, that's why I choose to leave it in this class. If we see the need for the primary signature's SignedCms in the countersignature we should consider moving it, but in the mean time I think it should stay here.

@PatoBeltran PatoBeltran merged commit 894388a into dev Feb 9, 2018
@PatoBeltran PatoBeltran deleted the dev-patobeltran-signing-repo-first branch February 9, 2018 06:02
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