Compare commits
2 Commits
master
...
p256-seria
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bfdc4bf2e | ||
|
|
e15cc2295a |
@@ -12,7 +12,6 @@ import (
|
|||||||
// Specification: https://w3c.github.io/cg-reports/credentials/CG-FINAL-di-eddsa-2020-20220724/
|
// Specification: https://w3c.github.io/cg-reports/credentials/CG-FINAL-di-eddsa-2020-20220724/
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MultibaseCode = uint64(0xed)
|
|
||||||
JsonLdContext = "https://w3id.org/security/suites/ed25519-2020/v1"
|
JsonLdContext = "https://w3id.org/security/suites/ed25519-2020/v1"
|
||||||
Type = "Ed25519VerificationKey2020"
|
Type = "Ed25519VerificationKey2020"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestJsonRoundTrip(t *testing.T) {
|
|||||||
require.JSONEq(t, data, string(bytes))
|
require.JSONEq(t, data, string(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignature(t *testing.T) {
|
func TestVerify(t *testing.T) {
|
||||||
// test vector from https://datatracker.ietf.org/doc/html/rfc8032#section-7.1
|
// test vector from https://datatracker.ietf.org/doc/html/rfc8032#section-7.1
|
||||||
|
|
||||||
pkHex := "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025"
|
pkHex := "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025"
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package ed25519
|
|||||||
import (
|
import (
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/INFURA/go-did/verifications/internal"
|
"github.com/INFURA/go-did/verifications/internal"
|
||||||
@@ -18,6 +20,8 @@ const (
|
|||||||
PrivateKeySize = ed25519.PrivateKeySize
|
PrivateKeySize = ed25519.PrivateKeySize
|
||||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||||
SignatureSize = ed25519.SignatureSize
|
SignatureSize = ed25519.SignatureSize
|
||||||
|
|
||||||
|
MultibaseCode = uint64(0xed)
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
func GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
||||||
@@ -25,12 +29,23 @@ func GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
|
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
// It errors if the slice is not the right size.
|
// It errors if the slice is not the right size.
|
||||||
func PublicKeyFromBytes(b []byte) (PublicKey, error) {
|
func PublicKeyFromBytes(b []byte) (PublicKey, error) {
|
||||||
if len(b) != PublicKeySize {
|
if len(b) != PublicKeySize {
|
||||||
return nil, fmt.Errorf("invalid ed25519 public key size")
|
return nil, fmt.Errorf("invalid ed25519 public key size")
|
||||||
}
|
}
|
||||||
return PublicKey(b), nil
|
// make a copy
|
||||||
|
return PublicKey(append([]byte{}, b...)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToBytes converts a public key to a byte slice.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
|
func PublicKeyToBytes(pub PublicKey) []byte {
|
||||||
|
// Copy the private key to a fixed size buffer that can get allocated on the
|
||||||
|
// caller's stack after inlining.
|
||||||
|
var buf [PublicKeySize]byte
|
||||||
|
return append(buf[:0], pub...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyFromMultibase decodes the public key from its Multibase form
|
// PublicKeyFromMultibase decodes the public key from its Multibase form
|
||||||
@@ -45,7 +60,7 @@ func PublicKeyFromMultibase(multibase string) (PublicKey, error) {
|
|||||||
if len(bytes) != PublicKeySize {
|
if len(bytes) != PublicKeySize {
|
||||||
return nil, fmt.Errorf("invalid ed25519 public key size")
|
return nil, fmt.Errorf("invalid ed25519 public key size")
|
||||||
}
|
}
|
||||||
return bytes, nil
|
return PublicKeyFromBytes(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase
|
// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase
|
||||||
@@ -53,13 +68,100 @@ func PublicKeyToMultibase(pub PublicKey) string {
|
|||||||
return helpers.MultibaseEncode(MultibaseCode, pub)
|
return helpers.MultibaseEncode(MultibaseCode, pub)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivateKeyFromBytes converts a serialized public key to a PrivateKey.
|
// PublicKeyFromX509DER decodes an X.509 DER (binary) encoded public key.
|
||||||
|
func PublicKeyFromX509DER(bytes []byte) (PublicKey, error) {
|
||||||
|
pub, err := x509.ParsePKIXPublicKey(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pub.(PublicKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToX509DER encodes the public key into the X.509 DER (binary) format.
|
||||||
|
func PublicKeyToX509DER(pub PublicKey) []byte {
|
||||||
|
res, _ := x509.MarshalPKIXPublicKey(pub)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const pemPubBlockType = "PUBLIC KEY"
|
||||||
|
|
||||||
|
// PublicKeyFromX509PEM decodes an X.509 PEM (string) encoded public key.
|
||||||
|
func PublicKeyFromX509PEM(str string) (PublicKey, error) {
|
||||||
|
block, _ := pem.Decode([]byte(str))
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode PEM block")
|
||||||
|
}
|
||||||
|
if block.Type != pemPubBlockType {
|
||||||
|
return nil, fmt.Errorf("incorrect PEM block type")
|
||||||
|
}
|
||||||
|
return PublicKeyFromX509DER(block.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToX509PEM encodes the public key into the X.509 PEM (binary) format.
|
||||||
|
func PublicKeyToX509PEM(pub PublicKey) string {
|
||||||
|
der := PublicKeyToX509DER(pub)
|
||||||
|
return string(pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: pemPubBlockType,
|
||||||
|
Bytes: der,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyFromBytes converts a serialized private key to a PrivateKey.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
// It errors if the slice is not the right size.
|
// It errors if the slice is not the right size.
|
||||||
func PrivateKeyFromBytes(b []byte) (PrivateKey, error) {
|
func PrivateKeyFromBytes(b []byte) (PrivateKey, error) {
|
||||||
if len(b) != ed25519.PrivateKeySize {
|
if len(b) != PrivateKeySize {
|
||||||
return nil, fmt.Errorf("invalid ed25519 private key size")
|
return nil, fmt.Errorf("invalid ed25519 private key size")
|
||||||
}
|
}
|
||||||
return b, nil
|
// make a copy
|
||||||
|
return append([]byte{}, b...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyToBytes converts a private key to a byte slice.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
|
func PrivateKeyToBytes(priv PrivateKey) []byte {
|
||||||
|
// Copy the private key to a fixed size buffer that can get allocated on the
|
||||||
|
// caller's stack after inlining.
|
||||||
|
var buf [PrivateKeySize]byte
|
||||||
|
return append(buf[:0], priv...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyFromPKCS8DER decodes a PKCS#8 DER (binary) encoded private key.
|
||||||
|
func PrivateKeyFromPKCS8DER(bytes []byte) (PrivateKey, error) {
|
||||||
|
priv, err := x509.ParsePKCS8PrivateKey(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return priv.(PrivateKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyToPKCS8DER encodes the private key into the PKCS#8 DER (binary) format.
|
||||||
|
func PrivateKeyToPKCS8DER(priv PrivateKey) []byte {
|
||||||
|
res, _ := x509.MarshalPKCS8PrivateKey(priv)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const pemPrivBlockType = "PRIVATE KEY"
|
||||||
|
|
||||||
|
// PrivateKeyFromPKCS8PEM decodes an PKCS#8 PEM (string) encoded private key.
|
||||||
|
func PrivateKeyFromPKCS8PEM(str string) (PrivateKey, error) {
|
||||||
|
block, _ := pem.Decode([]byte(str))
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode PEM block")
|
||||||
|
}
|
||||||
|
if block.Type != pemPrivBlockType {
|
||||||
|
return nil, fmt.Errorf("incorrect PEM block type")
|
||||||
|
}
|
||||||
|
return PrivateKeyFromPKCS8DER(block.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyToPKCS8PEM encodes the private key into the PKCS#8 PEM (binary) format.
|
||||||
|
func PrivateKeyToPKCS8PEM(priv PrivateKey) string {
|
||||||
|
der := PrivateKeyToPKCS8DER(priv)
|
||||||
|
return string(pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: pemPrivBlockType,
|
||||||
|
Bytes: der,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign signs the message with privateKey and returns a signature.
|
// Sign signs the message with privateKey and returns a signature.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ed25519_test
|
package ed25519_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -16,6 +17,23 @@ func TestGenerateKey(t *testing.T) {
|
|||||||
require.True(t, pub.Equal(priv.Public()))
|
require.True(t, pub.Equal(priv.Public()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBytesRoundTrip(t *testing.T) {
|
||||||
|
pub, priv, err := ed25519.GenerateKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
bytes := ed25519.PublicKeyToBytes(pub)
|
||||||
|
fmt.Println("pub", len(bytes))
|
||||||
|
rtPub, err := ed25519.PublicKeyFromBytes(bytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, pub.Equal(rtPub))
|
||||||
|
|
||||||
|
bytes = ed25519.PrivateKeyToBytes(priv)
|
||||||
|
fmt.Println("priv", len(bytes))
|
||||||
|
rtPriv, err := ed25519.PrivateKeyFromBytes(bytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, priv.Equal(rtPriv))
|
||||||
|
}
|
||||||
|
|
||||||
func TestMultibaseRoundTrip(t *testing.T) {
|
func TestMultibaseRoundTrip(t *testing.T) {
|
||||||
pub, _, err := ed25519.GenerateKeyPair()
|
pub, _, err := ed25519.GenerateKeyPair()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -25,3 +43,47 @@ func TestMultibaseRoundTrip(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, pub, rt)
|
require.Equal(t, pub, rt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPublicKeyX509RoundTrip(t *testing.T) {
|
||||||
|
pub, _, err := ed25519.GenerateKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
der := ed25519.PublicKeyToX509DER(pub)
|
||||||
|
fmt.Println("der", len(der))
|
||||||
|
rt, err := ed25519.PublicKeyFromX509DER(der)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, pub.Equal(rt))
|
||||||
|
|
||||||
|
pem := ed25519.PublicKeyToX509PEM(pub)
|
||||||
|
fmt.Println("pem", len(pem))
|
||||||
|
rt, err = ed25519.PublicKeyFromX509PEM(pem)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, pub.Equal(rt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrivateKeyPKCS8RoundTrip(t *testing.T) {
|
||||||
|
pub, priv, err := ed25519.GenerateKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
der := ed25519.PrivateKeyToPKCS8DER(priv)
|
||||||
|
fmt.Println("der", len(der))
|
||||||
|
rt, err := ed25519.PrivateKeyFromPKCS8DER(der)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, priv.Equal(rt))
|
||||||
|
require.True(t, pub.Equal(rt.Public()))
|
||||||
|
|
||||||
|
pem := ed25519.PrivateKeyToPKCS8PEM(priv)
|
||||||
|
fmt.Println("pem", len(pem))
|
||||||
|
rt, err = ed25519.PrivateKeyFromPKCS8PEM(pem)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, priv.Equal(rt))
|
||||||
|
require.True(t, pub.Equal(rt.Public()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// func TestSignature(t *testing.T) {
|
||||||
|
// pub, priv, err := ed25519.GenerateKeyPair()
|
||||||
|
// require.NoError(t, err)
|
||||||
|
//
|
||||||
|
// sig := ed25519.Sign(priv, []byte("message"))
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|||||||
188
verifications/p256/key.go
Normal file
188
verifications/p256/key.go
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
package p256
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
helpers "github.com/INFURA/go-did/verifications/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PublicKey = *ecdsa.PublicKey
|
||||||
|
type PrivateKey = *ecdsa.PrivateKey
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TODO
|
||||||
|
PublicKeySize = 33
|
||||||
|
PrivateKeySize = 32
|
||||||
|
SignatureSize = 123456
|
||||||
|
|
||||||
|
MultibaseCode = uint64(0x1200)
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
||||||
|
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return priv.Public().(PublicKey), priv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
|
// It errors if the slice is not the right size.
|
||||||
|
func PublicKeyFromBytes(b []byte) (PublicKey, error) {
|
||||||
|
if len(b) != PublicKeySize {
|
||||||
|
return nil, fmt.Errorf("invalid P-256 public key size")
|
||||||
|
}
|
||||||
|
x, y := elliptic.UnmarshalCompressed(elliptic.P256(), b)
|
||||||
|
if x == nil {
|
||||||
|
return nil, fmt.Errorf("invalid P-256 public key")
|
||||||
|
}
|
||||||
|
return &ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}, nil
|
||||||
|
|
||||||
|
// if len(b) != PublicKeySize {
|
||||||
|
// return nil, fmt.Errorf("invalid P-256 public key size")
|
||||||
|
// }
|
||||||
|
// x := new(big.Int).SetBytes(b[:PublicKeySize/2])
|
||||||
|
// y := new(big.Int).SetBytes(b[PublicKeySize/2:])
|
||||||
|
// return &ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToBytes converts a public key to a byte slice.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
|
func PublicKeyToBytes(pub PublicKey) []byte {
|
||||||
|
return elliptic.MarshalCompressed(elliptic.P256(), pub.X, pub.Y)
|
||||||
|
|
||||||
|
// // fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||||
|
// var buf [PublicKeySize]byte
|
||||||
|
// pub.X.FillBytes(buf[:PublicKeySize/2])
|
||||||
|
// pub.Y.FillBytes(buf[PublicKeySize/2:])
|
||||||
|
// return buf[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyFromMultibase decodes the public key from its Multibase form
|
||||||
|
func PublicKeyFromMultibase(multibase string) (PublicKey, error) {
|
||||||
|
code, bytes, err := helpers.MultibaseDecode(multibase)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if code != MultibaseCode {
|
||||||
|
return nil, fmt.Errorf("invalid code")
|
||||||
|
}
|
||||||
|
return PublicKeyFromBytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase
|
||||||
|
func PublicKeyToMultibase(pub PublicKey) string {
|
||||||
|
bytes := elliptic.MarshalCompressed(elliptic.P256(), pub.X, pub.Y)
|
||||||
|
return helpers.MultibaseEncode(MultibaseCode, bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyFromX509DER decodes an X.509 DER (binary) encoded public key.
|
||||||
|
func PublicKeyFromX509DER(bytes []byte) (PublicKey, error) {
|
||||||
|
pub, err := x509.ParsePKIXPublicKey(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pub.(PublicKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToX509DER encodes the public key into the X.509 DER (binary) format.
|
||||||
|
func PublicKeyToX509DER(pub PublicKey) []byte {
|
||||||
|
res, _ := x509.MarshalPKIXPublicKey(pub)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const pemPubBlockType = "PUBLIC KEY"
|
||||||
|
|
||||||
|
// PublicKeyFromX509PEM decodes an X.509 PEM (string) encoded public key.
|
||||||
|
func PublicKeyFromX509PEM(str string) (PublicKey, error) {
|
||||||
|
block, _ := pem.Decode([]byte(str))
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode PEM block")
|
||||||
|
}
|
||||||
|
if block.Type != pemPubBlockType {
|
||||||
|
return nil, fmt.Errorf("incorrect PEM block type")
|
||||||
|
}
|
||||||
|
return PublicKeyFromX509DER(block.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToX509PEM encodes the public key into the X.509 PEM (binary) format.
|
||||||
|
func PublicKeyToX509PEM(pub PublicKey) string {
|
||||||
|
der := PublicKeyToX509DER(pub)
|
||||||
|
return string(pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: pemPubBlockType,
|
||||||
|
Bytes: der,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyFromBytes converts a serialized public key to a PrivateKey.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
|
// It errors if the slice is not the right size.
|
||||||
|
func PrivateKeyFromBytes(b []byte) (PrivateKey, error) {
|
||||||
|
if len(b) != PrivateKeySize {
|
||||||
|
return nil, fmt.Errorf("invalid P-256 private key size")
|
||||||
|
}
|
||||||
|
|
||||||
|
res := &ecdsa.PrivateKey{
|
||||||
|
D: new(big.Int).SetBytes(b),
|
||||||
|
PublicKey: ecdsa.PublicKey{Curve: elliptic.P256()},
|
||||||
|
}
|
||||||
|
|
||||||
|
// recompute the public key
|
||||||
|
res.PublicKey.X, res.PublicKey.Y = res.PublicKey.Curve.ScalarBaseMult(b)
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyToBytes converts a private key to a byte slice.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
|
func PrivateKeyToBytes(priv PrivateKey) []byte {
|
||||||
|
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||||
|
var buf [PrivateKeySize]byte
|
||||||
|
priv.D.FillBytes(buf[:])
|
||||||
|
return buf[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyFromPKCS8DER decodes a PKCS#8 DER (binary) encoded private key.
|
||||||
|
func PrivateKeyFromPKCS8DER(bytes []byte) (PrivateKey, error) {
|
||||||
|
priv, err := x509.ParsePKCS8PrivateKey(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return priv.(PrivateKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyToPKCS8DER encodes the private key into the PKCS#8 DER (binary) format.
|
||||||
|
func PrivateKeyToPKCS8DER(priv PrivateKey) []byte {
|
||||||
|
res, _ := x509.MarshalPKCS8PrivateKey(priv)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const pemPrivBlockType = "PRIVATE KEY"
|
||||||
|
|
||||||
|
// PrivateKeyFromPKCS8PEM decodes an PKCS#8 PEM (string) encoded private key.
|
||||||
|
func PrivateKeyFromPKCS8PEM(str string) (PrivateKey, error) {
|
||||||
|
block, _ := pem.Decode([]byte(str))
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode PEM block")
|
||||||
|
}
|
||||||
|
if block.Type != pemPrivBlockType {
|
||||||
|
return nil, fmt.Errorf("incorrect PEM block type")
|
||||||
|
}
|
||||||
|
return PrivateKeyFromPKCS8DER(block.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyToPKCS8PEM encodes the private key into the PKCS#8 PEM (binary) format.
|
||||||
|
func PrivateKeyToPKCS8PEM(priv PrivateKey) string {
|
||||||
|
der := PrivateKeyToPKCS8DER(priv)
|
||||||
|
return string(pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: pemPrivBlockType,
|
||||||
|
Bytes: der,
|
||||||
|
}))
|
||||||
|
}
|
||||||
74
verifications/p256/key_test.go
Normal file
74
verifications/p256/key_test.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package p256_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/INFURA/go-did/verifications/p256"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGenerateKey(t *testing.T) {
|
||||||
|
pub, priv, err := p256.GenerateKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, pub)
|
||||||
|
require.NotNil(t, priv)
|
||||||
|
require.True(t, pub.Equal(priv.Public()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytesRoundTrip(t *testing.T) {
|
||||||
|
pub, priv, err := p256.GenerateKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
bytes := p256.PublicKeyToBytes(pub)
|
||||||
|
rtPub, err := p256.PublicKeyFromBytes(bytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, pub.Equal(rtPub))
|
||||||
|
|
||||||
|
bytes = p256.PrivateKeyToBytes(priv)
|
||||||
|
rtPriv, err := p256.PrivateKeyFromBytes(bytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, priv.Equal(rtPriv))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPublicKeyX509RoundTrip(t *testing.T) {
|
||||||
|
pub, _, err := p256.GenerateKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
der := p256.PublicKeyToX509DER(pub)
|
||||||
|
rt, err := p256.PublicKeyFromX509DER(der)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, pub.Equal(rt))
|
||||||
|
|
||||||
|
pem := p256.PublicKeyToX509PEM(pub)
|
||||||
|
rt, err = p256.PublicKeyFromX509PEM(pem)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, pub.Equal(rt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrivateKeyPKCS8RoundTrip(t *testing.T) {
|
||||||
|
pub, priv, err := p256.GenerateKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
der := p256.PrivateKeyToPKCS8DER(priv)
|
||||||
|
rt, err := p256.PrivateKeyFromPKCS8DER(der)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, priv.Equal(rt))
|
||||||
|
require.True(t, pub.Equal(rt.Public()))
|
||||||
|
|
||||||
|
pem := p256.PrivateKeyToPKCS8PEM(priv)
|
||||||
|
rt, err = p256.PrivateKeyFromPKCS8PEM(pem)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, priv.Equal(rt))
|
||||||
|
require.True(t, pub.Equal(rt.Public()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultibaseRoundTrip(t *testing.T) {
|
||||||
|
pub, _, err := p256.GenerateKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mb := p256.PublicKeyToMultibase(pub)
|
||||||
|
rt, err := p256.PublicKeyFromMultibase(mb)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, pub, rt)
|
||||||
|
}
|
||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
// Specification: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519
|
// Specification: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MultibaseCode = uint64(0xec)
|
|
||||||
JsonLdContext = "https://w3id.org/security/suites/x25519-2020/v1"
|
JsonLdContext = "https://w3id.org/security/suites/x25519-2020/v1"
|
||||||
Type = "X25519KeyAgreementKey2020"
|
Type = "X25519KeyAgreementKey2020"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"crypto/ecdh"
|
"crypto/ecdh"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
@@ -21,6 +23,8 @@ const (
|
|||||||
PrivateKeySize = 32
|
PrivateKeySize = 32
|
||||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||||
SignatureSize = 32
|
SignatureSize = 32
|
||||||
|
|
||||||
|
MultibaseCode = uint64(0xec)
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
func GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
||||||
@@ -32,11 +36,18 @@ func GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
|
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
// It errors if the slice is not the right size.
|
// It errors if the slice is not the right size.
|
||||||
func PublicKeyFromBytes(b []byte) (PublicKey, error) {
|
func PublicKeyFromBytes(b []byte) (PublicKey, error) {
|
||||||
return ecdh.X25519().NewPublicKey(b)
|
return ecdh.X25519().NewPublicKey(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PublicKeyToBytes converts a public key to a byte slice.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
|
func PublicKeyToBytes(pub PublicKey) []byte {
|
||||||
|
return pub.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
// PublicKeyFromEd25519 converts an ed25519 public key to a x25519 public key.
|
// PublicKeyFromEd25519 converts an ed25519 public key to a x25519 public key.
|
||||||
// It errors if the slice is not the right size.
|
// It errors if the slice is not the right size.
|
||||||
//
|
//
|
||||||
@@ -106,7 +117,7 @@ func PublicKeyFromMultibase(multibase string) (PublicKey, error) {
|
|||||||
if code != MultibaseCode {
|
if code != MultibaseCode {
|
||||||
return nil, fmt.Errorf("invalid code")
|
return nil, fmt.Errorf("invalid code")
|
||||||
}
|
}
|
||||||
return ecdh.X25519().NewPublicKey(bytes)
|
return PublicKeyFromBytes(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase
|
// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase
|
||||||
@@ -114,12 +125,58 @@ func PublicKeyToMultibase(pub PublicKey) string {
|
|||||||
return helpers.MultibaseEncode(MultibaseCode, pub.Bytes())
|
return helpers.MultibaseEncode(MultibaseCode, pub.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivateKeyFromBytes converts a serialized public key to a PrivateKey.
|
// PublicKeyFromX509DER decodes an X.509 DER (binary) encoded public key.
|
||||||
|
func PublicKeyFromX509DER(bytes []byte) (PublicKey, error) {
|
||||||
|
pub, err := x509.ParsePKIXPublicKey(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pub.(PublicKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToX509DER encodes the public key into the X.509 DER (binary) format.
|
||||||
|
func PublicKeyToX509DER(pub PublicKey) []byte {
|
||||||
|
res, _ := x509.MarshalPKIXPublicKey(pub)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const pemPubBlockType = "PUBLIC KEY"
|
||||||
|
|
||||||
|
// PublicKeyFromX509PEM decodes an X.509 PEM (string) encoded public key.
|
||||||
|
func PublicKeyFromX509PEM(str string) (PublicKey, error) {
|
||||||
|
block, _ := pem.Decode([]byte(str))
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode PEM block")
|
||||||
|
}
|
||||||
|
if block.Type != pemPubBlockType {
|
||||||
|
return nil, fmt.Errorf("incorrect PEM block type")
|
||||||
|
}
|
||||||
|
return PublicKeyFromX509DER(block.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToX509PEM encodes the public key into the X.509 PEM (binary) format.
|
||||||
|
func PublicKeyToX509PEM(pub PublicKey) string {
|
||||||
|
der := PublicKeyToX509DER(pub)
|
||||||
|
return string(pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: pemPubBlockType,
|
||||||
|
Bytes: der,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyFromBytes converts a serialized private key to a PrivateKey.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
// It errors if len(privateKey) is not [PrivateKeySize].
|
// It errors if len(privateKey) is not [PrivateKeySize].
|
||||||
func PrivateKeyFromBytes(b []byte) (PrivateKey, error) {
|
func PrivateKeyFromBytes(b []byte) (PrivateKey, error) {
|
||||||
|
// this already check the size of b
|
||||||
return ecdh.X25519().NewPrivateKey(b)
|
return ecdh.X25519().NewPrivateKey(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrivateKeyToBytes converts a private key to a byte slice.
|
||||||
|
// This compact serialization format is the raw key material, without metadata or structure.
|
||||||
|
func PrivateKeyToBytes(priv PrivateKey) []byte {
|
||||||
|
return priv.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
// PrivateKeyFromEd25519 converts an ed25519 private key to a x25519 private key.
|
// PrivateKeyFromEd25519 converts an ed25519 private key to a x25519 private key.
|
||||||
// It errors if the slice is not the right size.
|
// It errors if the slice is not the right size.
|
||||||
//
|
//
|
||||||
@@ -142,6 +199,44 @@ func PrivateKeyFromEd25519(priv ed25519.PrivateKey) (PrivateKey, error) {
|
|||||||
return ecdh.X25519().NewPrivateKey(h[:32])
|
return ecdh.X25519().NewPrivateKey(h[:32])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrivateKeyFromPKCS8DER decodes a PKCS#8 DER (binary) encoded private key.
|
||||||
|
func PrivateKeyFromPKCS8DER(bytes []byte) (PrivateKey, error) {
|
||||||
|
priv, err := x509.ParsePKCS8PrivateKey(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return priv.(PrivateKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyToPKCS8DER encodes the private key into the PKCS#8 DER (binary) format.
|
||||||
|
func PrivateKeyToPKCS8DER(priv PrivateKey) []byte {
|
||||||
|
res, _ := x509.MarshalPKCS8PrivateKey(priv)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const pemPrivBlockType = "PRIVATE KEY"
|
||||||
|
|
||||||
|
// PrivateKeyFromPKCS8PEM decodes an PKCS#8 PEM (string) encoded private key.
|
||||||
|
func PrivateKeyFromPKCS8PEM(str string) (PrivateKey, error) {
|
||||||
|
block, _ := pem.Decode([]byte(str))
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode PEM block")
|
||||||
|
}
|
||||||
|
if block.Type != pemPrivBlockType {
|
||||||
|
return nil, fmt.Errorf("incorrect PEM block type")
|
||||||
|
}
|
||||||
|
return PrivateKeyFromPKCS8DER(block.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyToPKCS8PEM encodes the private key into the PKCS#8 PEM (binary) format.
|
||||||
|
func PrivateKeyToPKCS8PEM(priv PrivateKey) string {
|
||||||
|
der := PrivateKeyToPKCS8DER(priv)
|
||||||
|
return string(pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: pemPrivBlockType,
|
||||||
|
Bytes: der,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
func reverseBytes(b []byte) []byte {
|
func reverseBytes(b []byte) []byte {
|
||||||
r := make([]byte, len(b))
|
r := make([]byte, len(b))
|
||||||
for i := 0; i < len(b); i++ {
|
for i := 0; i < len(b); i++ {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package x25519_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdh"
|
"crypto/ecdh"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -20,6 +21,23 @@ func TestGenerateKey(t *testing.T) {
|
|||||||
require.True(t, pub.Equal(priv.Public()))
|
require.True(t, pub.Equal(priv.Public()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBytesRoundTrip(t *testing.T) {
|
||||||
|
pub, priv, err := x25519.GenerateKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
bytes := x25519.PublicKeyToBytes(pub)
|
||||||
|
fmt.Println("pub", len(bytes))
|
||||||
|
rtPub, err := x25519.PublicKeyFromBytes(bytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, pub.Equal(rtPub))
|
||||||
|
|
||||||
|
bytes = x25519.PrivateKeyToBytes(priv)
|
||||||
|
fmt.Println("priv", len(bytes))
|
||||||
|
rtPriv, err := x25519.PrivateKeyFromBytes(bytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, priv.Equal(rtPriv))
|
||||||
|
}
|
||||||
|
|
||||||
func TestMultibaseRoundTrip(t *testing.T) {
|
func TestMultibaseRoundTrip(t *testing.T) {
|
||||||
pub, _, err := x25519.GenerateKeyPair()
|
pub, _, err := x25519.GenerateKeyPair()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -30,6 +48,42 @@ func TestMultibaseRoundTrip(t *testing.T) {
|
|||||||
require.Equal(t, pub, rt)
|
require.Equal(t, pub, rt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPublicKeyX509RoundTrip(t *testing.T) {
|
||||||
|
pub, _, err := ed25519.GenerateKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
der := ed25519.PublicKeyToX509DER(pub)
|
||||||
|
fmt.Println("der", len(der))
|
||||||
|
rt, err := ed25519.PublicKeyFromX509DER(der)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, pub.Equal(rt))
|
||||||
|
|
||||||
|
pem := ed25519.PublicKeyToX509PEM(pub)
|
||||||
|
fmt.Println("pem", len(pem))
|
||||||
|
rt, err = ed25519.PublicKeyFromX509PEM(pem)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, pub.Equal(rt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrivateKeyPKCS8RoundTrip(t *testing.T) {
|
||||||
|
pub, priv, err := ed25519.GenerateKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
der := ed25519.PrivateKeyToPKCS8DER(priv)
|
||||||
|
fmt.Println("der", len(der))
|
||||||
|
rt, err := ed25519.PrivateKeyFromPKCS8DER(der)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, priv.Equal(rt))
|
||||||
|
require.True(t, pub.Equal(rt.Public()))
|
||||||
|
|
||||||
|
pem := ed25519.PrivateKeyToPKCS8PEM(priv)
|
||||||
|
fmt.Println("pem", len(pem))
|
||||||
|
rt, err = ed25519.PrivateKeyFromPKCS8PEM(pem)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, priv.Equal(rt))
|
||||||
|
require.True(t, pub.Equal(rt.Public()))
|
||||||
|
}
|
||||||
|
|
||||||
func TestEd25519ToX25519(t *testing.T) {
|
func TestEd25519ToX25519(t *testing.T) {
|
||||||
// Known pubkey ed25519 --> x25519
|
// Known pubkey ed25519 --> x25519
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user