aboutsummaryrefslogtreecommitdiff
path: root/ed25519.go
diff options
context:
space:
mode:
Diffstat (limited to 'ed25519.go')
-rw-r--r--ed25519.go104
1 files changed, 104 insertions, 0 deletions
diff --git a/ed25519.go b/ed25519.go
new file mode 100644
index 0000000..602ac52
--- /dev/null
+++ b/ed25519.go
@@ -0,0 +1,104 @@
+package pki
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/rand"
+ "encoding/pem"
+ "errors"
+ "io"
+
+ "github.com/agl/ed25519"
+)
+
+const (
+ PemLabelEd25519 = "ED25519 PRIVATE KEY" // TODO find correct label
+)
+
+type (
+ Ed25519PrivateKey struct {
+ private_key [ed25519.PrivateKeySize]byte
+ }
+
+ Ed25519PublicKey struct {
+ public_key [ed25519.PublicKeySize]byte
+ }
+)
+
+// Create a new private key of type ed25519.
+func NewPrivateKeyEd25519() (*Ed25519PrivateKey, error) {
+ _, pr_raw, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+ return &Ed25519PrivateKey{*pr_raw}, nil
+}
+
+// Restore an ed25519 private key from a raw byte stream.
+// TODO does this have to be asn1? all other functions expect asn1
+func LoadPrivateKeyEd25519(raw []byte) (*Ed25519PrivateKey, error) {
+ var pr_loaded [ed25519.PrivateKeySize]byte
+ length := copy(pr_loaded[:], raw)
+ if length != ed25519.PrivateKeySize {
+ return nil, errors.New("private key length incorrect")
+ }
+ return &Ed25519PrivateKey{pr_loaded}, nil
+}
+
+// TODO implement the raw API for the private key
+func (pr *Ed25519PrivateKey) PrivateKey() crypto.PrivateKey {
+ return nil
+}
+
+// Return the public key for this private key.
+func (pr *Ed25519PrivateKey) Public() PublicKey {
+ buf := bytes.NewBufferString(string(pr.private_key[:])) // create a bytes buffer to read the private key
+ pu_raw, _, err := ed25519.GenerateKey(buf) // use the already built private key again
+ if err != nil {
+ return nil
+ }
+ return &Ed25519PublicKey{*pu_raw}
+}
+
+// Hash the message given the hash algorythm and sign the hash using the private key.
+func (pr *Ed25519PrivateKey) Sign(message []byte, hash crypto.Hash) ([]byte, error) {
+ hashed_message := hash.New()
+ hashed_message.Write(message)
+ result := ed25519.Sign(&pr.private_key, hashed_message.Sum(nil))[:]
+ return result, nil
+}
+
+// Export the private key into the Pem format.
+func (pr Ed25519PrivateKey) MarshalPem() (io.WriterTo, error) {
+ pem_block := pem.Block{Type: PemLabelEd25519, Bytes: pr.private_key[:]}
+ return marshalledPemBlock(pem.EncodeToMemory(&pem_block)), nil
+}
+
+// Load the public key from a raw byte stream.
+// TODO should this be read from ASN.1? All other functions do that.
+func LoadPublicKeyEd25519(raw []byte) (*Ed25519PublicKey, error) {
+ var pu_loaded [ed25519.PublicKeySize]byte
+ length := copy(pu_loaded[:], raw)
+ if length != ed25519.PublicKeySize {
+ return nil, errors.New("public key length incorrect")
+ }
+ return &Ed25519PublicKey{pu_loaded}, nil
+}
+
+// Export the public key into the pem format.
+func (pu Ed25519PublicKey) MarshalPem() (io.WriterTo, error) {
+ pem_block := pem.Block{Type: PemLabelPublic, Bytes: pu.public_key[:]}
+ return marshalledPemBlock(pem.EncodeToMemory(&pem_block)), nil
+}
+
+// Hash the message with the hash algorythm and check the signature against the result.
+func (pu Ed25519PublicKey) Verify(message []byte, signature []byte, hash crypto.Hash) (bool, error) {
+ var sig [ed25519.SignatureSize]byte
+ length := copy(sig[:], signature)
+ if length != ed25519.SignatureSize {
+ return false, errors.New("signature does not fit length")
+ }
+ hashed_message := hash.New()
+ hashed_message.Write(message)
+ return ed25519.Verify(&pu.public_key, hashed_message.Sum(nil), &sig), nil
+}