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

Add X.509 certificate parsing #1014

Merged
merged 63 commits into from
May 17, 2019
Merged

Conversation

bookmoons
Copy link
Contributor

@bookmoons bookmoons commented May 3, 2019

Adds certificate parsing under a new k6/crypto/x509 module. Methods are:
parse getAltNames getIssuer getSubject

Usage in JavaScript is like this:

import x509 from "k6/crypto/x509";
const pem = readCertificate();

const certificate = x509.parse(pem);
// certificate contains { subject, issuer, notBefore, notAfter, .. }

const altNames = x509.getAltNames(pem);
// altNames contains [ "ecxouncil.zz", "inquiries@excouncil.zz", .. ]

const issuer = x509.getIssuer(pem);
// issuer contains { country, organizationName, commonName, .. }

const subject = x509.getSubject(pem);
// subject contains ( country, organizationName, commonName, .. }

Semantics:

  • Missing values are encoded as empty strings.
  • Timestamps in notBefore notAfter are provided as ISO8601 strings. I think it's the easiest thing to deal with in JavaScript.

A few little changes:

  • Renamed countryName to country, because it's meant to hold the ISO country code.
  • It seems organizationalUnitName can have multiple values, so I've done it as an array of string.
  • publicKey.e is provided as a number literal.
  • Binary data is passed as byte arrays, for consistency with the open() interface on binary files. This applies to fingerPrint publicKey.n. fingerPrint is an SHA-1 hash which has a defined byte order. publicKey.n is big endian.

Toward #900.

@bookmoons
Copy link
Contributor Author

I'm also not sure if the original value returned from x509.ParseCertificate() shouldn't be saved in a private field somewhere in the Certificate struct, so we don't have to reconstitute it if we ever need to use the actual certificate in some of the other crypto operations mentioned in #900

Looked over the remaining operations. I don't think any of them use the certificate, so I think this is OK for now. Maybe it can be added if it ever becomes necessary.

Copy link
Member

@na-- na-- left a comment

Choose a reason for hiding this comment

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

I'm also not sure if the original value returned from x509.ParseCertificate() shouldn't be saved in a private field somewhere in the Certificate struct, so we don't have to reconstitute it if we ever need to use the actual certificate in some of the other crypto operations mentioned in #900

Looked over the remaining operations. I don't think any of them use the certificate, so I think this is OK for now. Maybe it can be added if it ever becomes necessary.

Yeah, agree, totally fine to leave it as it is if we don't use the certificate in the rest of the new APIs - we can easily add it if it's needed in the future.

js/modules/k6/crypto/x509/x509.go Show resolved Hide resolved
js/modules/k6/crypto/x509/x509.go Outdated Show resolved Hide resolved
@codecov-io
Copy link

Codecov Report

Merging #1014 into master will increase coverage by 0.17%.
The diff coverage is 91.2%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1014      +/-   ##
==========================================
+ Coverage   72.33%   72.51%   +0.17%     
==========================================
  Files         132      133       +1     
  Lines        9717     9808      +91     
==========================================
+ Hits         7029     7112      +83     
- Misses       2273     2278       +5     
- Partials      415      418       +3
Impacted Files Coverage Δ
js/modules/k6/crypto/x509/x509.go 91.2% <91.2%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1621aad...14806cb. Read the comment docs.

@codecov-io
Copy link

codecov-io commented May 10, 2019

Codecov Report

Merging #1014 into master will increase coverage by 0.33%.
The diff coverage is 98.4%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1014      +/-   ##
==========================================
+ Coverage   72.33%   72.66%   +0.33%     
==========================================
  Files         132      133       +1     
  Lines        9717     9842     +125     
==========================================
+ Hits         7029     7152     +123     
- Misses       2273     2274       +1     
- Partials      415      416       +1
Impacted Files Coverage Δ
js/modules/k6/crypto/x509/x509.go 98.4% <98.4%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1621aad...44cfa25. Read the comment docs.

@bookmoons
Copy link
Contributor Author

Pushed these changes.

This test-prev-golang build is hitting the network error. Tests from this PR pass.

The lint check is failing, but I can't seem to reproduce it. Running gofmt -s makes no changes to the file. Running the linter locally finds no issues. Not sure what to do about that one.

@bookmoons
Copy link
Contributor Author

Here's the complete structure we ended up with.

Certificate:

  • subject
    • country - string
    • postalCode - string
    • stateOrProvinceName - string
    • localityName - string
    • streetAddress - string
    • organizationName - string
    • organizationalUnitName - Array
    • commonName - string
    • names - Array
  • issuer:
    • country - string
    • stateOrProvinceName - string
    • localityName - string
    • organizationName - string
    • commonName - string
    • names - Array
  • notBefore - string, ISO8601 timestamp
  • notAfter - string, ISO8601 timestamp
  • altNames - Array
  • signatureAlgorithm - string, SignatureAlgorithm enum
  • fingerPrint - Array, byte array
  • publicKeyAlgorithm - string, PublicKeyAlgorithm enum
  • publicKey - PublicKey, combined structure

RDN: (= Relative Distinguished Name)

  • type - string
  • value - string

SignatureAlgorithm:

  • MD2-RSA
  • MD5-RSA
  • SHA1-RSA
  • SHA256-RSA
  • SHA384-RSA
  • SHA512-RSA
  • DSA-SHA1
  • DSA-SHA256
  • ECDSA-SHA1
  • ECDSA-SHA256
  • ECDSA-SHA384
  • ECDSA-SHA512
  • SHA256-RSAPSS
  • SHA384-RSAPSS
  • SHA512-RSAPSS
  • UnknownSignatureAlgorithm

PublicKeyAlgorithm:

  • RSA
  • DSA
  • ECDSA
  • UnknownPublicKeyAlgorithm

Copy link
Member

@na-- na-- left a comment

Choose a reason for hiding this comment

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

Sorry for the late review again 😞 This mostly looks good to me, only the PublicKey details I've noted inline are something that I'm unsure about currently.

Regarding the linting issue, I think you might have an old Go version, 1.11.x probably. Try to update to 1.12 and run gofmt -w -s on the file then. Doing it locally produces the following diff:

diff --git a/js/modules/k6/crypto/x509/x509.go b/js/modules/k6/crypto/x509/x509.go
index 0f89f26d..368b74c5 100644
--- a/js/modules/k6/crypto/x509/x509.go
+++ b/js/modules/k6/crypto/x509/x509.go
@@ -176,7 +176,7 @@ func makeSubject(subject pkix.Name) Subject {
                StreetAddress:          first(subject.StreetAddress),
                OrganizationName:       first(subject.Organization),
                OrganizationalUnitName: subject.OrganizationalUnit,
-               Names: makeRdns(subject.Names),
+               Names:                  makeRdns(subject.Names),
        }
 }

js/modules/k6/crypto/x509/x509.go Outdated Show resolved Hide resolved
@bookmoons
Copy link
Contributor Author

Regarding the linting issue, I think you might have an old Go version

Of course, should have thought of it. The latest made the change.

Copy link
Member

@na-- na-- left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@mstoykov mstoykov left a comment

Choose a reason for hiding this comment

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

I would prefer if the certificate contents are transferred as []byte from the start. This way in the future if we support non pem certificates we won't need to change it.
Also I would really like it if we can get 100% coverage on this. There are a couple of error paths that are not covered.

js/modules/k6/crypto/x509/x509.go Outdated Show resolved Hide resolved
@bookmoons
Copy link
Contributor Author

bookmoons commented May 17, 2019

Also I would really like it if we can get 100% coverage on this. There are a couple of error paths that are not covered.

I was able to hit all but 1 of these. The last one is not easily within reach. It requires a valid certificate that parses correctly yet contains an unsupported key. But we're supporting all available key types.

Is it possible that one could be left out? If it's important I can reorganize to make parseCertificate() interceptable.

Copy link
Member

@na-- na-- left a comment

Choose a reason for hiding this comment

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

No, LGTM, I think the current state is more than fine 😄

Copy link
Contributor

@mstoykov mstoykov left a comment

Choose a reason for hiding this comment

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

LGTM too

@na-- na-- merged commit 2999a1e into grafana:master May 17, 2019
@bookmoons
Copy link
Contributor Author

Thanks for reviewing guys. Will get signing rebased onto this and submit soon.

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.

5 participants