Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
e362e13
Changed the LoadPaths function.
yogisinha Jul 26, 2023
23ceed5
Adding a new function crypto.x509.parse_and_verify_certificates_with_…
yogisinha Feb 11, 2024
35d2f85
Added test case, using Go x509.VerifyOptions, fixed function descript…
yogisinha Feb 21, 2024
a1f123d
Merge branch 'main' into parse_and_verify_certificates_with_options
yogisinha Feb 21, 2024
4a472ff
Added auto generated builtins json files.
yogisinha Feb 21, 2024
290918c
Merge branch 'parse_and_verify_certificates_with_options' of https://…
yogisinha Feb 21, 2024
c91d9cd
Added changes as per suggestions.
yogisinha Feb 21, 2024
6ba2619
Added new lines
yogisinha Feb 21, 2024
a8ba1d1
Added new lines
yogisinha Feb 21, 2024
9c655c3
Added new lines
yogisinha Feb 21, 2024
6b32620
Added the new Array type for KeyUsages
yogisinha Feb 22, 2024
4f240dd
Adding a new function crypto.x509.parse_and_verify_certificates_with_…
yogisinha Feb 11, 2024
4597ffe
Added test case, using Go x509.VerifyOptions, fixed function descript…
yogisinha Feb 21, 2024
6556e4e
Added auto generated builtins json files.
yogisinha Feb 21, 2024
4562893
Added changes as per suggestions.
yogisinha Feb 21, 2024
2fe697c
Added new lines
yogisinha Feb 21, 2024
ce57d7a
Added new lines
yogisinha Feb 21, 2024
124047c
Added new lines
yogisinha Feb 21, 2024
444388d
Added the new Array type for KeyUsages
yogisinha Feb 22, 2024
8cc5838
Merge branch 'parse_and_verify_certificates_with_options' of https://…
yogisinha Feb 28, 2024
ee1819b
Merge branch 'main' into parse_and_verify_certificates_with_options
yogisinha Feb 29, 2024
797e401
Adding a new function crypto.x509.parse_and_verify_certificates_with_…
yogisinha Feb 11, 2024
4a40db9
Added test case, using Go x509.VerifyOptions, fixed function descript…
yogisinha Feb 21, 2024
d6331e8
Added auto generated builtins json files.
yogisinha Feb 21, 2024
9fffee2
Added changes as per suggestions.
yogisinha Feb 21, 2024
9bb2127
Added new lines
yogisinha Feb 21, 2024
6d47ba4
Added new lines
yogisinha Feb 21, 2024
5303e09
Added new lines
yogisinha Feb 21, 2024
6066aae
Added the new Array type for KeyUsages
yogisinha Feb 22, 2024
5eda48e
Adding a new function crypto.x509.parse_and_verify_certificates_with_…
yogisinha Mar 2, 2024
c8422fe
Changes as per suggestions.
yogisinha Mar 2, 2024
aa87a3a
Merge branch 'parse_and_verify_certificates_with_options' of https://…
yogisinha Mar 2, 2024
0436522
Changes as per suggestions.
yogisinha Mar 2, 2024
74b6730
Changes as per suggestions.
yogisinha Mar 6, 2024
458e963
Changes as per suggestions.
yogisinha Mar 6, 2024
dc6683a
Merge branch 'main' of https://github.com/open-policy-agent/opa into …
yogisinha Mar 10, 2024
a9b4bd8
Added the test case for parse cert with options.
yogisinha Mar 10, 2024
720b333
Changes as per suggestions.
yogisinha Mar 12, 2024
f992c0d
Shortening the function name.
yogisinha Mar 12, 2024
9effeab
Commenting out the function to check the deploy preview without this.
yogisinha Mar 13, 2024
ea77470
Merge branch 'main' of https://github.com/open-policy-agent/opa into …
yogisinha Mar 13, 2024
3c31aa6
Some changes.
yogisinha Mar 13, 2024
580fa87
Some changes.
yogisinha Mar 13, 2024
f9b196d
changes to test preview.
yogisinha Mar 13, 2024
5cad579
changes to test preview.
yogisinha Mar 13, 2024
49796ad
changes to test preview.
yogisinha Mar 13, 2024
8ebe57a
changes to test preview.
yogisinha Mar 13, 2024
ce4a226
Merge branch 'main' of https://github.com/open-policy-agent/opa into …
yogisinha Mar 18, 2024
3993618
Suggested changes.
yogisinha Mar 18, 2024
91e9007
Update generated code
Mar 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions ast/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ var DefaultBuiltins = [...]*Builtin{
// Crypto
CryptoX509ParseCertificates,
CryptoX509ParseAndVerifyCertificates,
CryptoX509ParseAndVerifyCertificatesWithOptions,
CryptoMd5,
CryptoSha1,
CryptoSha256,
Expand Down Expand Up @@ -2327,6 +2328,31 @@ with all others being treated as intermediates.`,
),
}

var CryptoX509ParseAndVerifyCertificatesWithOptions = &Builtin{
Name: "crypto.x509.parse_and_verify_certificates_with_options",
Description: `Returns one or more certificates from the given string containing PEM
or base64 encoded DER certificates after verifying the supplied certificates form a complete
certificate chain back to a trusted root. A config option passed as the second argument can
be used to configure the validation options used.

The first certificate is treated as the root and the last is treated as the leaf,
with all others being treated as intermediates.`,

Decl: types.NewFunction(
types.Args(
types.Named("certs", types.S).Description("base64 encoded DER or PEM data containing two or more certificates where the first is a root CA, the last is a leaf certificate, and all others are intermediate CAs"),
types.Named("options", types.NewObject(
nil,
types.NewDynamicProperty(types.S, types.A),
)).Description("object containing extra configs to verify the validity of certificates. `options` object supports four fields which maps to same fields in [x509.VerifyOptions struct](https://pkg.go.dev/crypto/x509#VerifyOptions). `DNSName`, `CurrentTime`: Nanoseconds since the Unix Epoch as a number, `MaxConstraintComparisons` and `KeyUsages`. `KeyUsages` is list and can have possible values as in: `\"KeyUsageAny\"`, `\"KeyUsageServerAuth\"`, `\"KeyUsageClientAuth\"`, `\"KeyUsageCodeSigning\"`, `\"KeyUsageEmailProtection\"`, `\"KeyUsageIPSECEndSystem\"`, `\"KeyUsageIPSECTunnel\"`, `\"KeyUsageIPSECUser\"`, `\"KeyUsageTimeStamping\"`, `\"KeyUsageOCSPSigning\"`, `\"KeyUsageMicrosoftServerGatedCrypto\"`, `\"KeyUsageNetscapeServerGatedCrypto\"`, `\"KeyUsageMicrosoftCommercialCodeSigning\"`, `\"KeyUsageMicrosoftKernelCodeSigning\"` "),
),
types.Named("output", types.NewArray([]types.Type{
types.B,
types.NewArray(nil, types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))),
}, nil)).Description("array of `[valid, certs]`: if the input certificate chain could be verified then `valid` is `true` and `certs` is an array of X.509 certificates represented as objects; if the input certificate chain could not be verified then `valid` is `false` and `certs` is `[]`"),
),
}

var CryptoX509ParseCertificateRequest = &Builtin{
Name: "crypto.x509.parse_certificate_request",
Description: "Returns a PKCS #10 certificate signing request from the given PEM-encoded PKCS#10 certificate signing request.",
Expand Down
26 changes: 26 additions & 0 deletions builtin_metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"crypto.sha1",
"crypto.sha256",
"crypto.x509.parse_and_verify_certificates",
"crypto.x509.parse_and_verify_certificates_with_options",
"crypto.x509.parse_certificate_request",
"crypto.x509.parse_certificates",
"crypto.x509.parse_keypair",
Expand Down Expand Up @@ -4417,6 +4418,31 @@
},
"wasm": false
},
"crypto.x509.parse_and_verify_certificates_with_options": {
"args": [
{
"description": "base64 encoded DER or PEM data containing two or more certificates where the first is a root CA, the last is a leaf certificate, and all others are intermediate CAs",
"name": "certs",
"type": "string"
},
{
"description": "object containing extra configs to verify the validity of certificates. `options` object supports four fields which maps to same fields in [x509.VerifyOptions struct](https://pkg.go.dev/crypto/x509#VerifyOptions). `DNSName`, `CurrentTime`: Nanoseconds since the Unix Epoch as a number, `MaxConstraintComparisons` and `KeyUsages`. `KeyUsages` is list and can have possible values as in: `\"KeyUsageAny\"`, `\"KeyUsageServerAuth\"`, `\"KeyUsageClientAuth\"`, `\"KeyUsageCodeSigning\"`, `\"KeyUsageEmailProtection\"`, `\"KeyUsageIPSECEndSystem\"`, `\"KeyUsageIPSECTunnel\"`, `\"KeyUsageIPSECUser\"`, `\"KeyUsageTimeStamping\"`, `\"KeyUsageOCSPSigning\"`, `\"KeyUsageMicrosoftServerGatedCrypto\"`, `\"KeyUsageNetscapeServerGatedCrypto\"`, `\"KeyUsageMicrosoftCommercialCodeSigning\"`, `\"KeyUsageMicrosoftKernelCodeSigning\"` ",
"name": "options",
"type": "object[string: any]"
}
],
"available": [
"edge"
],
"description": "Returns one or more certificates from the given string containing PEM\nor base64 encoded DER certificates after verifying the supplied certificates form a complete\ncertificate chain back to a trusted root. A config option passed as the second argument can\nbe used to configure the validation options used.\n\nThe first certificate is treated as the root and the last is treated as the leaf,\nwith all others being treated as intermediates.",
"introduced": "edge",
"result": {
"description": "array of `[valid, certs]`: if the input certificate chain could be verified then `valid` is `true` and `certs` is an array of X.509 certificates represented as objects; if the input certificate chain could not be verified then `valid` is `false` and `certs` is `[]`",
"name": "output",
"type": "array\u003cboolean, array[object[string: any]]\u003e"
},
"wasm": false
},
"crypto.x509.parse_certificate_request": {
"args": [
{
Expand Down
44 changes: 44 additions & 0 deletions capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,50 @@
"type": "function"
}
},
{
"name": "crypto.x509.parse_and_verify_certificates_with_options",
"decl": {
"args": [
{
"type": "string"
},
{
"dynamic": {
"key": {
"type": "string"
},
"value": {
"type": "any"
}
},
"type": "object"
}
],
"result": {
"static": [
{
"type": "boolean"
},
{
"dynamic": {
"dynamic": {
"key": {
"type": "string"
},
"value": {
"type": "any"
}
},
"type": "object"
},
"type": "array"
}
],
"type": "array"
},
"type": "function"
}
},
{
"name": "crypto.x509.parse_certificate_request",
"decl": {
Expand Down
161 changes: 157 additions & 4 deletions topdown/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"hash"
"os"
"strings"
"time"

"github.com/open-policy-agent/opa/internal/jwx/jwk"

Expand Down Expand Up @@ -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)
}
Expand All @@ -122,6 +123,153 @@ 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
}

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, ok := k.(string)
if !ok {
continue
}

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")
}

// 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 {
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 {
Expand Down Expand Up @@ -380,6 +528,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)
Expand All @@ -394,7 +543,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")
}
Expand All @@ -414,8 +563,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 {
Expand Down
Loading