-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Parse and verify certificates with options #6583
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
Changes from 47 commits
e362e13
23ceed5
35d2f85
a1f123d
4a472ff
290918c
c91d9cd
6ba2619
a8ba1d1
9c655c3
6b32620
4f240dd
4597ffe
6556e4e
4562893
2fe697c
ce57d7a
124047c
444388d
8cc5838
ee1819b
797e401
4a40db9
d6331e8
9fffee2
9bb2127
6d47ba4
5303e09
6066aae
5eda48e
c8422fe
aa87a3a
0436522
74b6730
458e963
dc6683a
a9b4bd8
720b333
f992c0d
9effeab
ea77470
3c31aa6
580fa87
f9b196d
5cad579
49796ad
8ebe57a
ce4a226
3993618
91e9007
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ import ( | |
| "hash" | ||
| "os" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/open-policy-agent/opa/internal/jwx/jwk" | ||
|
|
||
|
|
@@ -104,7 +105,7 @@ func builtinCryptoX509ParseAndVerifyCertificates(_ BuiltinContext, operands []*a | |
| return iter(invalid) | ||
| } | ||
|
|
||
| verified, err := verifyX509CertificateChain(certs) | ||
| verified, err := verifyX509CertificateChain(certs, x509.VerifyOptions{}) | ||
| if err != nil { | ||
| return iter(invalid) | ||
| } | ||
|
|
@@ -122,6 +123,150 @@ func builtinCryptoX509ParseAndVerifyCertificates(_ BuiltinContext, operands []*a | |
| return iter(valid) | ||
| } | ||
|
|
||
| var allowedKeyUsages = map[string]x509.ExtKeyUsage{ | ||
| "KeyUsageAny": x509.ExtKeyUsageAny, | ||
| "KeyUsageServerAuth": x509.ExtKeyUsageServerAuth, | ||
| "KeyUsageClientAuth": x509.ExtKeyUsageClientAuth, | ||
| "KeyUsageCodeSigning": x509.ExtKeyUsageCodeSigning, | ||
| "KeyUsageEmailProtection": x509.ExtKeyUsageEmailProtection, | ||
| "KeyUsageIPSECEndSystem": x509.ExtKeyUsageIPSECEndSystem, | ||
| "KeyUsageIPSECTunnel": x509.ExtKeyUsageIPSECTunnel, | ||
| "KeyUsageIPSECUser": x509.ExtKeyUsageIPSECUser, | ||
| "KeyUsageTimeStamping": x509.ExtKeyUsageTimeStamping, | ||
| "KeyUsageOCSPSigning": x509.ExtKeyUsageOCSPSigning, | ||
| "KeyUsageMicrosoftServerGatedCrypto": x509.ExtKeyUsageMicrosoftServerGatedCrypto, | ||
| "KeyUsageNetscapeServerGatedCrypto": x509.ExtKeyUsageNetscapeServerGatedCrypto, | ||
| "KeyUsageMicrosoftCommercialCodeSigning": x509.ExtKeyUsageMicrosoftCommercialCodeSigning, | ||
| "KeyUsageMicrosoftKernelCodeSigning": x509.ExtKeyUsageMicrosoftKernelCodeSigning, | ||
| } | ||
|
|
||
| func builtinCryptoX509ParseAndVerifyCertificatesWithOptions(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { | ||
|
|
||
| input, err := builtins.StringOperand(operands[0].Value, 1) | ||
| if err != nil { | ||
| return err | ||
| } | ||
yogisinha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| options, err := builtins.ObjectOperand(operands[1].Value, 2) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| invalid := ast.ArrayTerm( | ||
| ast.BooleanTerm(false), | ||
| ast.NewTerm(ast.NewArray()), | ||
| ) | ||
|
|
||
| certs, err := getX509CertsFromString(string(input)) | ||
| if err != nil { | ||
| return iter(invalid) | ||
| } | ||
|
|
||
| // Collect the cert verification options | ||
| verifyOpt, err := extractVerifyOpts(options) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| verified, err := verifyX509CertificateChain(certs, verifyOpt) | ||
| if err != nil { | ||
| return iter(invalid) | ||
| } | ||
|
|
||
| value, err := ast.InterfaceToValue(verified) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| valid := ast.ArrayTerm( | ||
| ast.BooleanTerm(true), | ||
| ast.NewTerm(value), | ||
| ) | ||
|
|
||
| return iter(valid) | ||
| } | ||
|
|
||
| func extractVerifyOpts(options ast.Object) (verifyOpt x509.VerifyOptions, err error) { | ||
|
|
||
| for _, key := range options.Keys() { | ||
| k, err := ast.JSON(key.Value) | ||
| if err != nil { | ||
| return verifyOpt, err | ||
| } | ||
| k = k.(string) | ||
|
||
|
|
||
| switch k { | ||
| case "DNSName": | ||
| dns, ok := options.Get(key).Value.(ast.String) | ||
| if ok { | ||
| verifyOpt.DNSName = strings.Trim(string(dns), "\"") | ||
| } else { | ||
| return verifyOpt, fmt.Errorf("'DNSName' should be a string") | ||
| } | ||
| case "CurrentTime": | ||
| c, ok := options.Get(key).Value.(ast.Number) | ||
| if ok { | ||
| nanosecs, ok := c.Int64() | ||
| if ok { | ||
| verifyOpt.CurrentTime = time.Unix(0, nanosecs) | ||
| } else { | ||
| return verifyOpt, fmt.Errorf("'CurrentTime' should be a valid int64 number") | ||
| } | ||
| } else { | ||
| return verifyOpt, fmt.Errorf("'CurrentTime' should be a number") | ||
| } | ||
| case "MaxConstraintComparisons": | ||
| c, ok := options.Get(key).Value.(ast.Number) | ||
| if ok { | ||
| maxComparisons, ok := c.Int() | ||
| if ok { | ||
| verifyOpt.MaxConstraintComparisions = maxComparisons | ||
| } else { | ||
| return verifyOpt, fmt.Errorf("'MaxConstraintComparisons' should be a valid number") | ||
| } | ||
| } else { | ||
| return verifyOpt, fmt.Errorf("'MaxConstraintComparisons' should be a number") | ||
| } | ||
| case "KeyUsages": | ||
| type forEach interface { | ||
| Foreach(func(*ast.Term)) | ||
| } | ||
| var ks forEach | ||
| switch options.Get(key).Value.(type) { | ||
| case *ast.Array: | ||
| ks = options.Get(key).Value.(*ast.Array) | ||
| case ast.Set: | ||
| ks = options.Get(key).Value.(ast.Set) | ||
| default: | ||
| return verifyOpt, fmt.Errorf("'KeyUsages' should be an Array or Set") | ||
| } | ||
|
|
||
yogisinha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // Collect the x509.ExtKeyUsage values by looking up the | ||
| // mapping of key usage strings to x509.ExtKeyUsage | ||
| var invalidKUsgs []string | ||
| ks.Foreach(func(t *ast.Term) { | ||
| u, ok := t.Value.(ast.String) | ||
| if ok { | ||
| v := strings.Trim(string(u), "\"") | ||
| if k, ok := allowedKeyUsages[v]; ok { | ||
yogisinha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| verifyOpt.KeyUsages = append(verifyOpt.KeyUsages, k) | ||
| } else { | ||
| invalidKUsgs = append(invalidKUsgs, v) | ||
| } | ||
| } | ||
| }) | ||
| if len(invalidKUsgs) > 0 { | ||
| return x509.VerifyOptions{}, fmt.Errorf("invalid entries for 'KeyUsages' found: %s", invalidKUsgs) | ||
| } | ||
| default: | ||
| return verifyOpt, fmt.Errorf("invalid key option") | ||
| } | ||
|
|
||
| } | ||
|
|
||
| return verifyOpt, nil | ||
| } | ||
|
|
||
| func builtinCryptoX509ParseKeyPair(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { | ||
| certificate, err := builtins.StringOperand(operands[0].Value, 1) | ||
| if err != nil { | ||
|
|
@@ -380,6 +525,7 @@ func builtinCryptoHmacEqual(_ BuiltinContext, operands []*ast.Term, iter func(*a | |
| func init() { | ||
| RegisterBuiltinFunc(ast.CryptoX509ParseCertificates.Name, builtinCryptoX509ParseCertificates) | ||
| RegisterBuiltinFunc(ast.CryptoX509ParseAndVerifyCertificates.Name, builtinCryptoX509ParseAndVerifyCertificates) | ||
| RegisterBuiltinFunc(ast.CryptoX509ParseAndVerifyCertificatesWithOptions.Name, builtinCryptoX509ParseAndVerifyCertificatesWithOptions) | ||
| RegisterBuiltinFunc(ast.CryptoMd5.Name, builtinCryptoMd5) | ||
| RegisterBuiltinFunc(ast.CryptoSha1.Name, builtinCryptoSha1) | ||
| RegisterBuiltinFunc(ast.CryptoSha256.Name, builtinCryptoSha256) | ||
|
|
@@ -394,7 +540,7 @@ func init() { | |
| RegisterBuiltinFunc(ast.CryptoHmacEqual.Name, builtinCryptoHmacEqual) | ||
| } | ||
|
|
||
| func verifyX509CertificateChain(certs []*x509.Certificate) ([]*x509.Certificate, error) { | ||
| func verifyX509CertificateChain(certs []*x509.Certificate, vo x509.VerifyOptions) ([]*x509.Certificate, error) { | ||
| if len(certs) < 2 { | ||
| return nil, builtins.NewOperandErr(1, "must supply at least two certificates to be able to verify") | ||
| } | ||
|
|
@@ -414,8 +560,12 @@ func verifyX509CertificateChain(certs []*x509.Certificate) ([]*x509.Certificate, | |
|
|
||
| // verify the cert chain back to the root | ||
| verifyOpts := x509.VerifyOptions{ | ||
| Roots: roots, | ||
| Intermediates: intermediates, | ||
| Roots: roots, | ||
| Intermediates: intermediates, | ||
| DNSName: vo.DNSName, | ||
| CurrentTime: vo.CurrentTime, | ||
| KeyUsages: vo.KeyUsages, | ||
| MaxConstraintComparisions: vo.MaxConstraintComparisions, | ||
| } | ||
| chains, err := leaf.Verify(verifyOpts) | ||
| if err != nil { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.