package cosesign1 import ( "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/pem" "errors" "fmt" "net/url" "strings" didx509resolver "github.com/Microsoft/hcsshim/internal/did-x509-resolver" "github.com/sirupsen/logrus" ) func parsePemChain(chainPem string) ([]*x509.Certificate, error) { var chain = []*x509.Certificate{} bs := []byte(chainPem) for block, rest := pem.Decode(bs); block != nil; block, rest = pem.Decode(rest) { if block.Type == "CERTIFICATE" { cert, err := x509.ParseCertificate(block.Bytes) if err != nil { return nil, fmt.Errorf("certificate parser failed: %w", err) } chain = append(chain, cert) } } return chain, nil } func MakeDidX509(fingerprintAlgorithm string, fingerprintIndex int, chainPEM string, didPolicy string, verbose bool) (string, error) { if fingerprintAlgorithm != "sha256" { return "", fmt.Errorf("unsupported fingerprint hash algorithm %q", fingerprintAlgorithm) } if fingerprintIndex < 1 { return "", fmt.Errorf("fingerprint index must be >= 1") } chain, err := parsePemChain(chainPEM) if err != nil { return "", err } if len(chain) < 1 { return "", fmt.Errorf("chain must not be empty") } if fingerprintIndex > len(chain)-1 { return "", fmt.Errorf("signer index out of bounds") } signerCert := chain[fingerprintIndex] hash := sha256.Sum256(signerCert.Raw) fingerprint := base64.RawURLEncoding.EncodeToString(hash[:]) var policyTokens []string didPolicyUpper := strings.ToUpper(didPolicy) switch didPolicyUpper { case "CN": policyTokens = append(policyTokens, "subject", "CN", chain[0].Subject.CommonName) case "EKU": // Note: In general there may be many predefined and not predefined key usages. // We pick the first non-predefined one, or, if there are none, the first predefined one. // Others must be specified manually (as in custom, next branch). // non-predefined if len(chain[0].UnknownExtKeyUsage) > 0 { policyTokens = append(policyTokens, "eku", chain[0].UnknownExtKeyUsage[0].String()) } else if len(chain[0].ExtKeyUsage) > 0 { extendedKeyUsage := chain[0].ExtKeyUsage[0] keyUsageOid, ok := didx509resolver.OidFromExtKeyUsage(extendedKeyUsage) // predefined if ok { policyTokens = append(policyTokens, "eku", keyUsageOid.String()) } } default: // Custom policies policyTokens = strings.Split(didPolicy, ":") } if len(policyTokens) == 0 { return "", errors.New("invalid policy") } for i := 0; i < len(policyTokens); i++ { policyName := policyTokens[i] switch policyName { case "subject": i += 2 if i >= len(policyTokens) { return "", fmt.Errorf("invalid %q policy", policyName) } policyTokens[i] = url.PathEscape(policyTokens[i]) default: } } r := "did:x509:0:" + fingerprintAlgorithm + ":" + fingerprint + "::" + strings.Join(policyTokens, ":") _, err = didx509resolver.Resolve(chainPEM, r, true) if err != nil { return "", err } if verbose { logrus.Debug("did:x509 resolved correctly") } return r, nil }