Skip to content

Commit

Permalink
feat: first change for ECDH-1PU crypto primtive
Browse files Browse the repository at this point in the history
First change to introduce ECDH-1PU primitive:
Includes new key/primitive types, key managers, protobufs, first change of (incomplete) key wrapping logic.
Includes introduction of composite_common proto file for sharing the comment key type for ECDH-ES and ECHD-1PU.

Follow up changes will include primitive factories, key templates and 1PU implementation.

part of hyperledger-archives#1806

Signed-off-by: Baha Shaaban <[email protected]>
  • Loading branch information
Baha Shaaban committed Jun 8, 2020
1 parent a10d75a commit 6275735
Show file tree
Hide file tree
Showing 43 changed files with 3,079 additions and 211 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package subtle
package composite

// package subtle provides the core crypto primitives to be used by composite primitives. It is intended for internal
// use only.
// package composite provides the core crypto composite primitives such as ECDH-ES and ECDH-1PU to be used by JWE crypto

// EncryptedData represents the Encryption's output data as a result of ECDHESEncrypt.Encrypt(pt, aad) call
// The user of the primitive must unmarshal the result and build their own ECDH-ES compliant message (ie JWE msg)
Expand Down
107 changes: 107 additions & 0 deletions pkg/crypto/tinkcrypto/primitive/composite/ecdh1pu/ecdh1pu.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

// Package ecdh1pu provides implementations of payload encryption using ECDH-1PU KW key wrapping with AEAD primitives.
//
// The functionality of ecdh1pu Encryption is represented as a pair of
// primitives (interfaces):
//
// * ECDH1PUEncrypt for encryption of data and aad for a given list of recipients keys
//
// * ECDH1PUDecrypt for decryption of data for a certain recipient key and returning decrypted plaintext
//
//
// Example:
//
// package main
//
// import (
// "bytes"
//
// "github.com/google/tink/go/keyset"
//
// "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/composite/ecdh1pu/subtle"
// "github.com/aries-framework-go/pkg/crypto/tinkcrypto/composite/ecdh1pu"
// )
//
// func main() {
// // create recipient side keyset handle
// recKH, err := keyset.NewHandle(ecdh1pu.ECDH1PU256KWAES256GCMKeyTemplate())
// if err != nil {
// //handle error
// }
//
// // extract recipient public keyset handle and key
// recPubKH, err := recKH.Public()
// if err != nil {
// //handle error
// }
//
// buf := new(bytes.Buffer)
// pubKeyWriter := ecdh1pu.NewWriter(buf)
// err = recPubKH.WriteWithNoSecrets(pubKeyWriter)
// if err != nil {
// //handle error
// }
//
// ecPubKey := new(subtle.ECPublicKey)
// err := json.Unmarshal(buf.Bytes(), ecPubKey)
//
// // now create sender keyset handle with recipient public key (ecPubKey)
// sKH, err := keyset.NewHandle(ECDH1PU256KWAES256GCMKeyTemplateWithRecipients(
// []subtle.ECPublicKey{*ecPubKey}))
// if err != nil {
// // handle error
// }
//
// // for more recipient keys pass in a list: []subtle.ECPublicKey{*ecPubKey1, *ecPubKey2, *ecPubKey3, etc.})
// // at least 1 recipient is required.
//
// // extract sender public keyset handle to encrypt
// senderPubKH, err := sKH.Public()
// if err != nil {
// //handle error
// }
//
// e := ecdh1pu.NewECDH1PUEncrypt(senderPubKH)
//
// ct, err = e.Encrypt([]byte("secret message"), []byte("some aad"))
// if err != nil {
// // handle error
// }
//
// // get a handle on the decryption key material for a recipient
// // this is usually reloading the recipient's keyset handle (ie: `recKH` above) from a kms
// refRecKH , err := keyset.NewHandle( .....reference/rebuild `recKH` here...);
// d := ecdh1pu.NewECDH1PUDecrypt(refRecKH)
//
// pt, err := d.Decrypt(ct)
// if err != nil {
// // handle error
// }
// }
package ecdh1pu

import (
"fmt"

"github.com/google/tink/go/core/registry"
)

// TODO - find a better way to setup tink than init.
// nolint: gochecknoinits
func init() {
// TODO - avoid the tink registry singleton (if possible).
err := registry.RegisterKeyManager(newECDH1PUPrivateKeyManager())
if err != nil {
panic(fmt.Sprintf("ecdh1pu.init() failed: %v", err))
}

err = registry.RegisterKeyManager(newECDH1PUPublicKeyManager())
if err != nil {
panic(fmt.Sprintf("ecdh1pu.init() failed: %v", err))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package ecdh1pu

import (
"crypto/elliptic"
"fmt"

"github.com/golang/protobuf/proto"
"github.com/google/tink/go/core/registry"
hybrid "github.com/google/tink/go/hybrid/subtle"
"github.com/google/tink/go/keyset"
tinkpb "github.com/google/tink/go/proto/tink_go_proto"

"github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/composite/ecdh1pu/subtle"
commonpb "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/proto/common_composite_go_proto"
ecdh1pupb "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/proto/ecdh1pu_aead_go_proto"
)

const (
ecdh1puAESPrivateKeyVersion = 0
ecdh1puAESPrivateKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.Ecdh1puAesAeadPrivateKey"
)

// common errors
var errInvalidECDH1PUAESPrivateKey = fmt.Errorf("ecdh1pu_aes_private_key_manager: invalid key")
var errInvalidECDH1PUAESPrivateKeyFormat = fmt.Errorf("ecdh1pu_aes_private_key_manager: invalid key format")

// ecdh1puAESPrivateKeyManager is an implementation of PrivateKeyManager interface.
// It generates new ECDHESPrivateKey (AES) keys and produces new instances of ECDH1PUAEADCompositeDecrypt subtle.
type ecdh1puAESPrivateKeyManager struct{}

// Assert that ecdh1puAESPrivateKeyManager implements the PrivateKeyManager interface.
var _ registry.PrivateKeyManager = (*ecdh1puAESPrivateKeyManager)(nil)

// newECDH1PUPrivateKeyManager creates a new ecdh1puAESPrivateKeyManager.
func newECDH1PUPrivateKeyManager() *ecdh1puAESPrivateKeyManager {
return new(ecdh1puAESPrivateKeyManager)
}

// Primitive creates an ECDHESPrivateKey subtle for the given serialized ECDHESPrivateKey proto.
func (km *ecdh1puAESPrivateKeyManager) Primitive(serializedKey []byte) (interface{}, error) {
if len(serializedKey) == 0 {
return nil, errInvalidECDH1PUAESPrivateKey
}

key := new(ecdh1pupb.Ecdh1PuAeadPrivateKey)

err := proto.Unmarshal(serializedKey, key)
if err != nil {
return nil, errInvalidECDH1PUAESPrivateKey
}

curve, err := km.validateKey(key)
if err != nil {
return nil, errInvalidECDH1PUAESPrivateKey
}

pvt := hybrid.GetECPrivateKey(curve, key.KeyValue)

rEnc, err := newRegisterECDH1PUAEADEncHelper(key.PublicKey.Params.EncParams.AeadEnc)
if err != nil {
return nil, err
}

ptFormat := key.PublicKey.Params.EcPointFormat.String()

return subtle.NewECDH1PUAEADCompositeDecrypt(pvt, ptFormat, rEnc, commonpb.KeyType_EC), nil
}

// NewKey creates a new key according to the specification of ECDH1PUPrivateKey format.
func (km *ecdh1puAESPrivateKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) {
if len(serializedKeyFormat) == 0 {
return nil, errInvalidECDH1PUAESPrivateKeyFormat
}

keyFormat := new(ecdh1pupb.Ecdh1PuAeadKeyFormat)

err := proto.Unmarshal(serializedKeyFormat, keyFormat)
if err != nil {
return nil, errInvalidECDH1PUAESPrivateKeyFormat
}

curve, err := validateKeyFormat(keyFormat.Params)
if err != nil {
return nil, errInvalidECDH1PUAESPrivateKeyFormat
}

keyFormat.Params.KwParams.KeyType = commonpb.KeyType_EC

pvt, err := hybrid.GenerateECDHKeyPair(curve)
if err != nil {
return nil, err
}

return &ecdh1pupb.Ecdh1PuAeadPrivateKey{
Version: ecdh1puAESPrivateKeyVersion,
KeyValue: pvt.D.Bytes(),
PublicKey: &ecdh1pupb.Ecdh1PuAeadPublicKey{
Version: ecdh1puAESPrivateKeyVersion,
Params: keyFormat.Params,
X: pvt.PublicKey.Point.X.Bytes(),
Y: pvt.PublicKey.Point.Y.Bytes(),
},
}, nil
}

// NewKeyData creates a new KeyData according to the specification of ECDHESPrivateKey Format.
// It should be used solely by the key management API.
func (km *ecdh1puAESPrivateKeyManager) NewKeyData(serializedKeyFormat []byte) (*tinkpb.KeyData, error) {
key, err := km.NewKey(serializedKeyFormat)
if err != nil {
return nil, err
}

serializedKey, err := proto.Marshal(key)
if err != nil {
return nil, err
}

return &tinkpb.KeyData{
TypeUrl: ecdh1puAESPrivateKeyTypeURL,
Value: serializedKey,
KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PRIVATE,
}, nil
}

// PublicKeyData returns the enclosed public key data of serializedPrivKey
func (km *ecdh1puAESPrivateKeyManager) PublicKeyData(serializedPrivKey []byte) (*tinkpb.KeyData, error) {
privKey := new(ecdh1pupb.Ecdh1PuAeadPrivateKey)

err := proto.Unmarshal(serializedPrivKey, privKey)
if err != nil {
return nil, errInvalidECDH1PUAESPrivateKey
}

serializedPubKey, err := proto.Marshal(privKey.PublicKey)
if err != nil {
return nil, errInvalidECDH1PUAESPrivateKey
}

return &tinkpb.KeyData{
TypeUrl: ecdh1puAESPublicKeyTypeURL,
Value: serializedPubKey,
KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PUBLIC,
}, nil
}

// DoesSupport indicates if this key manager supports the given key type.
func (km *ecdh1puAESPrivateKeyManager) DoesSupport(typeURL string) bool {
return typeURL == ecdh1puAESPrivateKeyTypeURL
}

// TypeURL returns the key type of keys managed by this key manager.
func (km *ecdh1puAESPrivateKeyManager) TypeURL() string {
return ecdh1puAESPrivateKeyTypeURL
}

// validateKey validates the given ECDH1PUPrivateKey and returns the KW curve.
func (km *ecdh1puAESPrivateKeyManager) validateKey(key *ecdh1pupb.Ecdh1PuAeadPrivateKey) (elliptic.Curve, error) {
err := keyset.ValidateKeyVersion(key.Version, ecdh1puAESPrivateKeyVersion)
if err != nil {
return nil, fmt.Errorf("ecdh1pu_private_key_manager: invalid key: %s", err)
}

return validateKeyFormat(key.PublicKey.Params)
}

// validateKeyFormat validates the given ECDHESKeyFormat and returns the KW Curve.
func validateKeyFormat(params *ecdh1pupb.Ecdh1PuAeadParams) (elliptic.Curve, error) {
c, err := hybrid.GetCurve(params.KwParams.CurveType.String())
if err != nil {
return nil, err
}

km, err := registry.GetKeyManager(params.EncParams.AeadEnc.TypeUrl)
if err != nil {
return nil, err
}

_, err = km.NewKeyData(params.EncParams.AeadEnc.Value)
if err != nil {
return nil, err
}

return c, nil
}
Loading

0 comments on commit 6275735

Please sign in to comment.