nuts-foundation/nuts-node

View on GitHub
vcr/credential/util.go

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
/*
 * Copyright (C) 2023 Nuts community
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

package credential

import (
    "errors"
    "github.com/nuts-foundation/go-did/did"
    "github.com/nuts-foundation/go-did/vc"
    "time"
)

// ResolveSubjectDID resolves the subject DID from the given credentials.
// It returns an error if:
// - the credentials do not have the same subject DID.
// - the credentials do not have a subject DID.
func ResolveSubjectDID(credentials ...vc.VerifiableCredential) (*did.DID, error) {
    var subjectID did.DID
    for _, credential := range credentials {
        sid, err := credential.SubjectDID()
        if err != nil {
            return nil, err
        }
        if !subjectID.Empty() && !subjectID.Equals(*sid) {
            return nil, errors.New("not all VCs have the same credentialSubject.id")
        }
        subjectID = *sid
    }
    return &subjectID, nil
}

// PresenterIsCredentialSubject checks if the presenter of the VP is the same as the subject of the VCs being presented.
// If the presentation signer or credential subject can't be resolved, it returns an error.
// If parsing succeeds and the signer DID is the same as the credential subject DID, it returns the DID.
func PresenterIsCredentialSubject(vp vc.VerifiablePresentation) (*did.DID, error) {
    signerDID, err := PresentationSigner(vp)
    if err != nil {
        return nil, err
    }
    credentialSubjectID, err := ResolveSubjectDID(vp.VerifiableCredential...)
    if err != nil {
        return nil, err
    }
    if !credentialSubjectID.Equals(*signerDID) {
        return nil, nil
    }
    return signerDID, nil
}

// PresentationIssuanceDate returns the date at which the presentation was issued.
// For JSON-LD, it looks at the first LinkedData proof's 'created' property.
// For JWT, it looks at the 'nbf' claim, or if that is not present, the 'iat' claim.
// If it can't resolve the date, it returns nil.
func PresentationIssuanceDate(presentation vc.VerifiablePresentation) *time.Time {
    var result time.Time
    switch presentation.Format() {
    case vc.JWTPresentationProofFormat:
        jwt := presentation.JWT()
        if result = jwt.NotBefore(); result.IsZero() {
            result = jwt.IssuedAt()
        }
    case vc.JSONLDPresentationProofFormat:
        ldProof, err := ParseLDProof(presentation)
        if err != nil {
            return nil
        }
        result = ldProof.Created
    }
    if result.IsZero() {
        return nil
    }
    return &result
}

// PresentationExpirationDate returns the date at which the presentation was issued.
// For JSON-LD, it looks at the first LinkedData proof's 'expires' property.
// For JWT, it looks at the 'exp' claim.
// If it can't resolve the date, it returns nil.
func PresentationExpirationDate(presentation vc.VerifiablePresentation) *time.Time {
    var result time.Time
    switch presentation.Format() {
    case vc.JWTPresentationProofFormat:
        result = presentation.JWT().Expiration()
    case vc.JSONLDPresentationProofFormat:
        ldProof, err := ParseLDProof(presentation)
        if err != nil || ldProof.Expires == nil {
            return nil
        }
        result = *ldProof.Expires
    }
    if result.IsZero() {
        return nil
    }
    return &result
}