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

feat: Legacy encrypt (current aries RFC 19), compatible with ACA-Py #292

Merged
merged 1 commit into from
Sep 20, 2019
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
module github.com/hyperledger/aries-framework-go

require (
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.1.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
Expand Down
183 changes: 183 additions & 0 deletions pkg/didcomm/crypto/legacy/authcrypt/authcrypt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package authcrypt

import (
"crypto/rand"
"errors"
"fmt"
"io"

"github.com/agl/ed25519/extra25519"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/nacl/box"
)

type privateEd25519 [ed25519.PrivateKeySize]byte
type publicEd25519 [ed25519.PublicKeySize]byte

type keyPairEd25519 struct {
priv *privateEd25519
pub *publicEd25519
}

// CurveKeySize is the size of public and private Curve25519 keys in bytes
const CurveKeySize int = 32

type privateCurve25519 [CurveKeySize]byte
type publicCurve25519 [CurveKeySize]byte

type keyPairCurve25519 struct {
priv *privateCurve25519
pub *publicCurve25519
}

// Crypter represents an Authcrypt Encrypter (Decrypter) that outputs/reads legacy Aries envelopes
type Crypter struct {
sender keyPairEd25519
recipients []*publicEd25519
randSource io.Reader
}

// New will create a Crypter that encrypts messages using the legacy Aries format
// Note: legacy crypter does not support XChacha20Poly1035 (XC20P), only Chacha20Poly1035 (C20P)
func New(sender keyPairEd25519, recipients []*publicEd25519) (*Crypter, error) { // nolint: lll

if len(recipients) == 0 {
return nil, errors.New("empty recipients keys, must have at least one recipient")
}

c := &Crypter{
sender: sender,
recipients: recipients,
randSource: rand.Reader,
}

if !isKeyPairValid(sender) {
return nil, fmt.Errorf(
"sender keyPair not supported, it must have a %d byte private key and %d byte public key",
ed25519.PrivateKeySize, ed25519.PublicKeySize)
}

return c, nil
}

func (c *Crypter) setRandSource(source io.Reader) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is unused

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used in tests, but I can replace it with direct access to the Crypter.

c.randSource = source
}

func isKeyPairValid(kp keyPairEd25519) bool {
if kp.priv == nil || kp.pub == nil {
return false
}

return true
}

// envelope is the full payload envelope for the JSON message
type envelope struct {
Protected string `json:"protected,omitempty"`
IV string `json:"iv,omitempty"`
CipherText string `json:"ciphertext,omitempty"`
Tag string `json:"tag,omitempty"`
}

// protected is the protected header of the JSON envelope
type protected struct {
Enc string `json:"enc,omitempty"`
Typ string `json:"typ,omitempty"`
Alg string `json:"alg,omitempty"`
Recipients []recipient `json:"recipients,omitempty"`
}

// recipient holds the data for a recipient in the envelope header
type recipient struct {
EncryptedKey string `json:"encrypted_key,omitempty"`
Header recipientHeader `json:"header,omitempty"`
}

// recipientHeader holds the header data for a recipient
type recipientHeader struct {
KID string `json:"kid,omitempty"`
Sender string `json:"sender,omitempty"`
IV string `json:"iv,omitempty"`
}

// publicEd25519toCurve25519 takes an Ed25519 public key and provides the corresponding Curve25519 public key
// This function wraps PublicKeyToCurve25519 from Adam Langley's ed25519 repo: https://github.com/agl/ed25519
func publicEd25519toCurve25519(pub *publicEd25519) (*publicCurve25519, error) {
if pub == nil {
return nil, errors.New("key is nil")
}
pkOut := new([CurveKeySize]byte)
success := extra25519.PublicKeyToCurve25519(pkOut, (*[ed25519.PublicKeySize]byte)(pub))
if !success {
return nil, errors.New("failed to convert public key")
}
return (*publicCurve25519)(pkOut), nil
}

// secretEd25519toCurve25519 converts a secret key from Ed25519 to curve25519 format
// This function wraps PrivateKeyToCurve25519 from Adam Langley's ed25519 repo: https://github.com/agl/ed25519
func secretEd25519toCurve25519(priv *privateEd25519) (*privateCurve25519, error) {
if priv == nil {
return nil, errors.New("key is nil")
}
sKOut := new([CurveKeySize]byte)
extra25519.PrivateKeyToCurve25519(sKOut, (*[ed25519.PrivateKeySize]byte)(priv))
return (*privateCurve25519)(sKOut), nil
}

func makeNonce(pub1, pub2 []byte) ([]byte, error) {
var nonce [24]byte
// generate an equivalent nonce to libsodium's (see link above)
nonceWriter, err := blake2b.New(24, nil)
if err != nil {
return nil, err
}
_, err = nonceWriter.Write(pub1)
if err != nil {
return nil, err
}
_, err = nonceWriter.Write(pub2)
if err != nil {
return nil, err
}

nonceOut := nonceWriter.Sum(nil)
copy(nonce[:], nonceOut)

return nonce[:], nil
}

// sodiumBoxSeal will encrypt a msg (in the case of this package, it will be
// an ephemeral key concatenated to the sender's public key) using the
// recipient's pubKey, this is equivalent to libsodium's C function: crypto_box_seal()
// https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes#usage
func sodiumBoxSeal(msg []byte, recPub *publicCurve25519, randSource io.Reader) ([]byte, error) {
var nonce [24]byte
// generate ephemeral curve25519 asymmetric keys
epk, esk, err := box.GenerateKey(randSource)
if err != nil {
return nil, err
}
// generate an equivalent nonce to libsodium's (see link above)
nonceSlice, err := makeNonce(epk[:], recPub[:])
if err != nil {
return nil, err
}
copy(nonce[:], nonceSlice)

var out = make([]byte, len(epk))
copy(out, epk[:])

// now seal the msg with the ephemeral key, nonce and recPub (which is recipient's publicKey)
ret := box.Seal(out, msg, &nonce, (*[32]byte)(recPub), esk)

return ret, nil
}
Loading