Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

feat: Sign credential - support BBS+ (API) #2601

Merged
merged 1 commit into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
157 changes: 155 additions & 2 deletions pkg/controller/command/verifiable/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ import (
"io"
"strings"

"github.com/piprate/json-gold/ld"

"github.com/hyperledger/aries-framework-go/pkg/common/log"
"github.com/hyperledger/aries-framework-go/pkg/controller/command"
"github.com/hyperledger/aries-framework-go/pkg/controller/internal/cmdutil"
ariescrypto "github.com/hyperledger/aries-framework-go/pkg/crypto"
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/doc/presexch"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld"
verifiablesigner "github.com/hyperledger/aries-framework-go/pkg/doc/signature/signer"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/bbsblssignature2020"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/ed25519signature2018"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/jsonwebsignature2020"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
Expand Down Expand Up @@ -123,6 +127,9 @@ const (
// JSONWebSignature2020 json web signature suite.
JSONWebSignature2020 = "JsonWebSignature2020"

// BbsBlsSignature2020 BBS signature suite.
BbsBlsSignature2020 = "BbsBlsSignature2020"

// Ed25519KeyType ed25519 key type.
Ed25519KeyType = "Ed25519"

Expand All @@ -133,6 +140,8 @@ const (
Ed25519VerificationKey = "Ed25519VerificationKey"
)

const bbsContext = "https://w3id.org/security/bbs/v1"

type provable interface {
AddLinkedDataProof(context *verifiable.LinkedDataProofContext, jsonldOpts ...jsonld.ProcessorOpts) error
}
Expand All @@ -144,6 +153,7 @@ type keyResolver interface {
type kmsSigner struct {
keyHandle interface{}
crypto ariescrypto.Crypto
bbs bool
}

func newKMSSigner(keyManager kms.KeyManager, c ariescrypto.Crypto, creator string) (*kmsSigner, error) {
Expand All @@ -161,7 +171,24 @@ func newKMSSigner(keyManager kms.KeyManager, c ariescrypto.Crypto, creator strin
return &kmsSigner{keyHandle: keyHandler, crypto: c}, nil
}

func (s *kmsSigner) textToLines(txt string) [][]byte {
lines := strings.Split(txt, "\n")
linesBytes := make([][]byte, 0, len(lines))

for i := range lines {
if strings.TrimSpace(lines[i]) != "" {
linesBytes = append(linesBytes, []byte(lines[i]))
}
}

return linesBytes
}

func (s *kmsSigner) Sign(data []byte) ([]byte, error) {
if s.bbs {
return s.crypto.SignMulti(s.textToLines(string(data)), s.keyHandle)
}

v, err := s.crypto.Sign(data, s.keyHandle)
if err != nil {
return nil, err
Expand Down Expand Up @@ -832,13 +859,22 @@ func (o *Command) addLinkedDataProof(p provable, opts *ProofOptions) error {
signatureSuite = ed25519signature2018.New(suite.WithSigner(s))
case JSONWebSignature2020:
signatureSuite = jsonwebsignature2020.New(suite.WithSigner(s))
case BbsBlsSignature2020:
s.bbs = true
signatureSuite = bbsblssignature2020.New(suite.WithSigner(s))
default:
return fmt.Errorf("signature type unsupported %s", opts.SignatureType)
}

signatureRepresentation := verifiable.SignatureJWS

if opts.SignatureRepresentation == nil {
opts.SignatureRepresentation = &signatureRepresentation
}

signingCtx := &verifiable.LinkedDataProofContext{
VerificationMethod: opts.VerificationMethod,
SignatureRepresentation: verifiable.SignatureJWS,
SignatureRepresentation: *opts.SignatureRepresentation,
SignatureType: opts.SignatureType,
Suite: signatureSuite,
Created: opts.Created,
Expand All @@ -847,7 +883,12 @@ func (o *Command) addLinkedDataProof(p provable, opts *ProofOptions) error {
Purpose: opts.proofPurpose,
}

err = p.AddLinkedDataProof(signingCtx)
bbsLoader, err := bbsJSONLDDocumentLoader()
if err != nil {
return err
}

err = p.AddLinkedDataProof(signingCtx, jsonld.WithDocumentLoader(bbsLoader))
if err != nil {
return fmt.Errorf("failed to add linked data proof: %w", err)
}
Expand Down Expand Up @@ -1038,3 +1079,115 @@ func getProofPurpose(method did.VerificationRelationship) (string, error) {

return "assertionMethod", nil
}

// TODO: context should not be loaded here, the loader should be defined once for the whole system.
func bbsJSONLDDocumentLoader() (*ld.CachingDocumentLoader, error) {
loader := presexch.CachingJSONLDLoader()

reader, err := ld.DocumentFromReader(strings.NewReader(contextBBSContent))
if err != nil {
return nil, err
}

loader.AddDocument(bbsContext, reader)

return loader, nil
}

const contextBBSContent = `{
"@context": {
"@version": 1.1,
"id": "@id",
"type": "@type",
"ldssk": "https://w3id.org/security#",
"BbsBlsSignature2020": {
"@id": "https://w3id.org/security#BbsBlsSignature2020",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"challenge": "sec:challenge",
"created": {
"@id": "http://purl.org/dc/terms/created",
"@type": "xsd:dateTime"
},
"domain": "sec:domain",
"proofValue": "sec:proofValue",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"assertionMethod": {
"@id": "sec:assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "sec:authenticationMethod",
"@type": "@id",
"@container": "@set"
}
}
},
"verificationMethod": {
"@id": "sec:verificationMethod",
"@type": "@id"
}
}
},
"BbsBlsSignatureProof2020": {
"@id": "https://w3id.org/security#BbsBlsSignatureProof2020",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"challenge": "sec:challenge",
"created": {
"@id": "http://purl.org/dc/terms/created",
"@type": "xsd:dateTime"
},
"domain": "sec:domain",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"assertionMethod": {
"@id": "sec:assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "sec:authenticationMethod",
"@type": "@id",
"@container": "@set"
}
}
},
"proofValue": "sec:proofValue",
"verificationMethod": {
"@id": "sec:verificationMethod",
"@type": "@id"
}
}
},
"Bls12381G2Key2020": "ldssk:Bls12381G2Key2020"
}
}`
74 changes: 71 additions & 3 deletions pkg/controller/command/verifiable/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,35 @@ const vc = `
}
`

const bbsVc = `{
"@context":[
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1",
"https://w3id.org/security/bbs/v1"
],
"credentialSubject":{
"degree":{
"degree":"MIT",
"degreeSchool":"MIT school",
"type":"BachelorDegree"
},
"id":"did:example:b34ca6cd37bbf23",
"name":"Jayden Doe",
"spouse":"did:example:c276e12ec21ebfeb1f712ebc6f1"
},
"description":"Government of Example Permanent Resident Card.",
"expirationDate":"2022-03-04T11:53:29.728412319+02:00",
"id":"https://issuer.oidp.uscis.gov/credentials/83627465",
"identifier":"83627465",
"issuanceDate":"2021-03-04T11:53:29.728412269+02:00",
"issuer":"did:example:489398593",
"name":"Permanent Resident Card",
"type":[
"VerifiableCredential",
"UniversityDegreeCredential"
]
}`

//nolint:lll
const vcWithDIDNotAvailble = `{
"@context":[
Expand Down Expand Up @@ -2073,6 +2102,47 @@ func TestCommand_SignCredential(t *testing.T) {
require.Contains(t, vc.Proofs[0]["type"], "JsonWebSignature2020")
})

t.Run("test sign credential with proof options - success (BbsBlsSignature2020)", func(t *testing.T) {
createdTime := time.Now().AddDate(-1, 0, 0)
signatureRepresentation := verifiable.SignatureProofValue

req := SignCredentialRequest{
Credential: []byte(bbsVc),
ProofOptions: &ProofOptions{
Domain: "issuer.example.com",
Challenge: "sample-random-test-value",
SignatureRepresentation: &signatureRepresentation,
Created: &createdTime,
SignatureType: BbsBlsSignature2020,
},
}

reqBytes, err := json.Marshal(req)
require.NoError(t, err)

var b bytes.Buffer
err = cmd.SignCredential(&b, bytes.NewBuffer(reqBytes))
require.NoError(t, err)

// verify response
var response SignCredentialResponse
err = json.NewDecoder(&b).Decode(&response)
require.NoError(t, err)
require.NotEmpty(t, response)

vc, err := verifiable.ParseCredential(response.VerifiableCredential, verifiable.WithDisabledProofCheck())

require.NoError(t, err)
require.NotNil(t, vc)
require.NotEmpty(t, vc.Proofs)
require.Len(t, vc.Proofs, 1)
require.Equal(t, vc.Proofs[0]["challenge"], req.Challenge)
require.Equal(t, vc.Proofs[0]["domain"], req.Domain)
require.Equal(t, vc.Proofs[0]["proofPurpose"], "assertionMethod")
require.Contains(t, vc.Proofs[0]["created"], strconv.Itoa(req.Created.Year()))
require.Contains(t, vc.Proofs[0]["type"], "BbsBlsSignature2020")
})

t.Run("test sign credential with proof options - success (ed25519 jsonwebsignature)", func(t *testing.T) {
createdTime := time.Now().AddDate(-1, 0, 0)
req := SignCredentialRequest{
Expand Down Expand Up @@ -2556,9 +2626,7 @@ func signVCWithBBS(r *require.Assertions, vc *verifiable.Credential) string {
pubKeyBytes, err := pubKey.Marshal()
r.NoError(err)

methodID := fingerprint.KeyFingerprint(0xeb, pubKeyBytes)
didKey := fmt.Sprintf("did:key:%s", methodID)
keyID := fmt.Sprintf("%s#%s", didKey, methodID)
didKey, keyID := fingerprint.CreateDIDKeyByCode(fingerprint.BLS12381g2PubKeyMultiCodec, pubKeyBytes)

bbsSigner, err := newBBSSigner(privKey)
r.NoError(err)
Expand Down
4 changes: 3 additions & 1 deletion pkg/controller/command/verifiable/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"encoding/json"
"time"

docverifiable "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
"github.com/hyperledger/aries-framework-go/pkg/store/verifiable"
)

Expand Down Expand Up @@ -40,7 +41,8 @@ type IDArg struct {
// ProofOptions is model to allow the dynamic proofing options by the user.
type ProofOptions struct {
// VerificationMethod is the URI of the verificationMethod used for the proof.
VerificationMethod string `json:"verificationMethod,omitempty"`
VerificationMethod string `json:"verificationMethod,omitempty"`
SignatureRepresentation *docverifiable.SignatureRepresentation `json:"signatureRepresentation,omitempty"`
// Created date of the proof. If omitted current system time will be used.
Created *time.Time `json:"created,omitempty"`
// Domain is operational domain of a digital proof.
Expand Down
4 changes: 1 addition & 3 deletions pkg/controller/rest/verifiable/operation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1400,9 +1400,7 @@ func signVCWithBBS(r *require.Assertions, vc *verifiableapi.Credential) string {
pubKeyBytes, err := pubKey.Marshal()
r.NoError(err)

methodID := fingerprint.KeyFingerprint(0xeb, pubKeyBytes)
didKey := fmt.Sprintf("did:key:%s", methodID)
keyID := fmt.Sprintf("%s#%s", didKey, methodID)
didKey, keyID := fingerprint.CreateDIDKeyByCode(fingerprint.BLS12381g2PubKeyMultiCodec, pubKeyBytes)

bbsSigner, err := newBBSSigner(privKey)
r.NoError(err)
Expand Down
12 changes: 3 additions & 9 deletions pkg/didcomm/protocol/middleware/presentproof/middlewares.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@ func defaultPdOptions() *pdOptions {

// AddBBSProofFn add BBS+ proof to the Presentation.
func AddBBSProofFn(p Provider) func(presentation *verifiable.Presentation) error {
const bls12381g2pub = 0xeb

km, cr := p.KMS(), p.Crypto()

return func(presentation *verifiable.Presentation) error {
Expand All @@ -153,19 +151,15 @@ func AddBBSProofFn(p Provider) func(presentation *verifiable.Presentation) error
return err
}

methodID := fingerprint.KeyFingerprint(bls12381g2pub, pubKey)
didKey := fmt.Sprintf("%s#%s", fmt.Sprintf("did:key:%s", methodID), methodID)
_, didKey := fingerprint.CreateDIDKeyByCode(fingerprint.BLS12381g2PubKeyMultiCodec, pubKey)

presentation.Context = append(presentation.Context, bbsContext)

return presentation.AddLinkedDataProof(&verifiable.LinkedDataProofContext{
SignatureType: "BbsBlsSignature2020",
SignatureRepresentation: verifiable.SignatureProofValue,
Suite: bbsblssignature2020.New(
suite.WithSigner(newBBSSigner(km, cr, kid)),
suite.WithVerifier(bbsblssignature2020.NewG2PublicKeyVerifier()),
),
VerificationMethod: didKey,
Suite: bbsblssignature2020.New(suite.WithSigner(newBBSSigner(km, cr, kid))),
VerificationMethod: didKey,
}, jsonld.WithDocumentLoader(bbsLoader))
}
}
Expand Down
Loading