mirror of
https://github.com/FiloSottile/age.git
synced 2026-03-11 08:55:41 +00:00
internal/age: refactor to use the new golang.org/x/crypto/curve25519 API
This commit is contained in:
parent
9821fcefc9
commit
03f7237541
6 changed files with 84 additions and 197 deletions
|
|
@ -7,10 +7,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
|
|
@ -86,18 +84,8 @@ func (i *EncryptedSSHIdentity) Matches(block *format.Recipient) error {
|
|||
if len(block.Args) != 1 {
|
||||
return fmt.Errorf("invalid %v recipient block", i.Type())
|
||||
}
|
||||
hash, err := format.DecodeString(block.Args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse %v recipient: %v", i.Type(), err)
|
||||
}
|
||||
if len(hash) != 4 {
|
||||
return fmt.Errorf("invalid %v recipient block", i.Type())
|
||||
}
|
||||
|
||||
sH := sha256.New()
|
||||
sH.Write(i.pubKey.Marshal())
|
||||
hh := sH.Sum(nil)
|
||||
if !bytes.Equal(hh[:4], hash) {
|
||||
if block.Args[0] != age.SSHFingerprint(i.pubKey) {
|
||||
return age.ErrIncorrectIdentity
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -20,21 +20,22 @@ import (
|
|||
const helloWorld = "Hello, Twitch!"
|
||||
|
||||
func TestEncryptDecryptX25519(t *testing.T) {
|
||||
var secretKeyA, publicKeyA, secretKeyB, publicKeyB [32]byte
|
||||
if _, err := rand.Read(secretKeyA[:]); err != nil {
|
||||
secretKeyA := make([]byte, curve25519.ScalarSize)
|
||||
secretKeyB := make([]byte, curve25519.ScalarSize)
|
||||
if _, err := rand.Read(secretKeyA); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := rand.Read(secretKeyB[:]); err != nil {
|
||||
if _, err := rand.Read(secretKeyB); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
curve25519.ScalarBaseMult(&publicKeyA, &secretKeyA)
|
||||
curve25519.ScalarBaseMult(&publicKeyB, &secretKeyB)
|
||||
publicKeyA, _ := curve25519.X25519(secretKeyA, curve25519.Basepoint)
|
||||
publicKeyB, _ := curve25519.X25519(secretKeyB, curve25519.Basepoint)
|
||||
|
||||
rA, err := age.NewX25519Recipient(publicKeyA[:])
|
||||
rA, err := age.NewX25519Recipient(publicKeyA)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rB, err := age.NewX25519Recipient(publicKeyB[:])
|
||||
rB, err := age.NewX25519Recipient(publicKeyB)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -52,7 +53,7 @@ func TestEncryptDecryptX25519(t *testing.T) {
|
|||
|
||||
t.Logf("%s", buf.Bytes())
|
||||
|
||||
i, err := age.NewX25519Identity(secretKeyB[:])
|
||||
i, err := age.NewX25519Identity(secretKeyB)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,17 +19,17 @@ import (
|
|||
)
|
||||
|
||||
func TestX25519RoundTrip(t *testing.T) {
|
||||
var secretKey, publicKey [32]byte
|
||||
if _, err := rand.Read(secretKey[:]); err != nil {
|
||||
secretKey := make([]byte, curve25519.ScalarSize)
|
||||
if _, err := rand.Read(secretKey); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
curve25519.ScalarBaseMult(&publicKey, &secretKey)
|
||||
publicKey, _ := curve25519.X25519(secretKey, curve25519.Basepoint)
|
||||
|
||||
r, err := age.NewX25519Recipient(publicKey[:])
|
||||
r, err := age.NewX25519Recipient(publicKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
i, err := age.NewX25519Identity(secretKey[:])
|
||||
i, err := age.NewX25519Identity(secretKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -39,10 +39,10 @@ func TestX25519RoundTrip(t *testing.T) {
|
|||
}
|
||||
|
||||
fileKey := make([]byte, 16)
|
||||
if _, err := rand.Read(fileKey[:]); err != nil {
|
||||
if _, err := rand.Read(fileKey); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block, err := r.Wrap(fileKey[:])
|
||||
block, err := r.Wrap(fileKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -55,8 +55,8 @@ func TestX25519RoundTrip(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(fileKey[:], out) {
|
||||
t.Errorf("invalid output: %x, expected %x", out, fileKey[:])
|
||||
if !bytes.Equal(fileKey, out) {
|
||||
t.Errorf("invalid output: %x, expected %x", out, fileKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,10 +78,10 @@ func TestScryptRoundTrip(t *testing.T) {
|
|||
}
|
||||
|
||||
fileKey := make([]byte, 16)
|
||||
if _, err := rand.Read(fileKey[:]); err != nil {
|
||||
if _, err := rand.Read(fileKey); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block, err := r.Wrap(fileKey[:])
|
||||
block, err := r.Wrap(fileKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -94,8 +94,8 @@ func TestScryptRoundTrip(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(fileKey[:], out) {
|
||||
t.Errorf("invalid output: %x, expected %x", out, fileKey[:])
|
||||
if !bytes.Equal(fileKey, out) {
|
||||
t.Errorf("invalid output: %x, expected %x", out, fileKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -123,10 +123,10 @@ func TestSSHRSARoundTrip(t *testing.T) {
|
|||
}
|
||||
|
||||
fileKey := make([]byte, 16)
|
||||
if _, err := rand.Read(fileKey[:]); err != nil {
|
||||
if _, err := rand.Read(fileKey); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block, err := r.Wrap(fileKey[:])
|
||||
block, err := r.Wrap(fileKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -139,8 +139,8 @@ func TestSSHRSARoundTrip(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(fileKey[:], out) {
|
||||
t.Errorf("invalid output: %x, expected %x", out, fileKey[:])
|
||||
if !bytes.Equal(fileKey, out) {
|
||||
t.Errorf("invalid output: %x, expected %x", out, fileKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -168,10 +168,10 @@ func TestSSHEd25519RoundTrip(t *testing.T) {
|
|||
}
|
||||
|
||||
fileKey := make([]byte, 16)
|
||||
if _, err := rand.Read(fileKey[:]); err != nil {
|
||||
if _, err := rand.Read(fileKey); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block, err := r.Wrap(fileKey[:])
|
||||
block, err := r.Wrap(fileKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -184,7 +184,7 @@ func TestSSHEd25519RoundTrip(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(fileKey[:], out) {
|
||||
t.Errorf("invalid output: %x, expected %x", out, fileKey[:])
|
||||
if !bytes.Equal(fileKey, out) {
|
||||
t.Errorf("invalid output: %x, expected %x", out, fileKey)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
package age
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
|
|
@ -25,6 +24,13 @@ import (
|
|||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func SSHFingerprint(pk ssh.PublicKey) string {
|
||||
h := sha256.New()
|
||||
h.Write(pk.Marshal())
|
||||
hh := h.Sum(nil)
|
||||
return format.EncodeToString(hh[:4])
|
||||
}
|
||||
|
||||
const oaepLabel = "age-tool.com ssh-rsa"
|
||||
|
||||
type SSHRSARecipient struct {
|
||||
|
|
@ -57,13 +63,9 @@ func NewSSHRSARecipient(pk ssh.PublicKey) (*SSHRSARecipient, error) {
|
|||
}
|
||||
|
||||
func (r *SSHRSARecipient) Wrap(fileKey []byte) (*format.Recipient, error) {
|
||||
h := sha256.New()
|
||||
h.Write(r.sshKey.Marshal())
|
||||
hh := h.Sum(nil)
|
||||
|
||||
l := &format.Recipient{
|
||||
Type: "ssh-rsa",
|
||||
Args: []string{format.EncodeToString(hh[:4])},
|
||||
Args: []string{SSHFingerprint(r.sshKey)},
|
||||
}
|
||||
|
||||
wrappedKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader,
|
||||
|
|
@ -103,18 +105,8 @@ func (i *SSHRSAIdentity) Unwrap(block *format.Recipient) ([]byte, error) {
|
|||
if len(block.Args) != 1 {
|
||||
return nil, errors.New("invalid ssh-rsa recipient block")
|
||||
}
|
||||
hash, err := format.DecodeString(block.Args[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse ssh-rsa recipient: %v", err)
|
||||
}
|
||||
if len(hash) != 4 {
|
||||
return nil, errors.New("invalid ssh-rsa recipient block")
|
||||
}
|
||||
|
||||
h := sha256.New()
|
||||
h.Write(i.sshKey.Marshal())
|
||||
hh := h.Sum(nil)
|
||||
if !bytes.Equal(hh[:4], hash) {
|
||||
if block.Args[0] != SSHFingerprint(i.sshKey) {
|
||||
return nil, ErrIncorrectIdentity
|
||||
}
|
||||
|
||||
|
|
@ -128,7 +120,7 @@ func (i *SSHRSAIdentity) Unwrap(block *format.Recipient) ([]byte, error) {
|
|||
|
||||
type SSHEd25519Recipient struct {
|
||||
sshKey ssh.PublicKey
|
||||
theirPublicKey [32]byte
|
||||
theirPublicKey []byte
|
||||
}
|
||||
|
||||
var _ Recipient = &SSHEd25519Recipient{}
|
||||
|
|
@ -145,8 +137,7 @@ func NewSSHEd25519Recipient(pk ssh.PublicKey) (*SSHEd25519Recipient, error) {
|
|||
|
||||
if pk, ok := pk.(ssh.CryptoPublicKey); ok {
|
||||
if pk, ok := pk.CryptoPublicKey().(ed25519.PublicKey); ok {
|
||||
pubKey := ed25519PublicKeyToCurve25519(pk)
|
||||
copy(r.theirPublicKey[:], pubKey)
|
||||
r.theirPublicKey = ed25519PublicKeyToCurve25519(pk)
|
||||
} else {
|
||||
return nil, errors.New("unexpected public key type")
|
||||
}
|
||||
|
|
@ -200,7 +191,7 @@ func ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) []byte {
|
|||
u := y.Mul(y.Add(y, big.NewInt(1)), denom)
|
||||
u.Mod(u, curve25519P)
|
||||
|
||||
out := make([]byte, 32)
|
||||
out := make([]byte, curve25519.PointSize)
|
||||
uBytes := u.Bytes()
|
||||
for i, b := range uBytes {
|
||||
out[len(uBytes)-i-1] = b
|
||||
|
|
@ -212,35 +203,37 @@ func ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) []byte {
|
|||
const ed25519Label = "age-tool.com ssh-ed25519"
|
||||
|
||||
func (r *SSHEd25519Recipient) Wrap(fileKey []byte) (*format.Recipient, error) {
|
||||
// TODO: DRY this up with the X25519 implementation.
|
||||
var ephemeral, ourPublicKey [32]byte
|
||||
if _, err := rand.Read(ephemeral[:]); err != nil {
|
||||
ephemeral := make([]byte, curve25519.ScalarSize)
|
||||
if _, err := rand.Read(ephemeral); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ourPublicKey, err := curve25519.X25519(ephemeral, curve25519.Basepoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
curve25519.ScalarBaseMult(&ourPublicKey, &ephemeral)
|
||||
|
||||
var sharedSecret, tweak [32]byte
|
||||
sharedSecret, err := curve25519.X25519(ephemeral, r.theirPublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tweak := make([]byte, curve25519.ScalarSize)
|
||||
tH := hkdf.New(sha256.New, nil, r.sshKey.Marshal(), []byte(ed25519Label))
|
||||
if _, err := io.ReadFull(tH, tweak[:]); err != nil {
|
||||
if _, err := io.ReadFull(tH, tweak); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
curve25519.ScalarMult(&sharedSecret, &ephemeral, &r.theirPublicKey)
|
||||
curve25519.ScalarMult(&sharedSecret, &tweak, &sharedSecret)
|
||||
|
||||
sH := sha256.New()
|
||||
sH.Write(r.sshKey.Marshal())
|
||||
hh := sH.Sum(nil)
|
||||
sharedSecret, _ = curve25519.X25519(tweak, sharedSecret)
|
||||
|
||||
l := &format.Recipient{
|
||||
Type: "ssh-ed25519",
|
||||
Args: []string{format.EncodeToString(hh[:4]),
|
||||
Args: []string{SSHFingerprint(r.sshKey),
|
||||
format.EncodeToString(ourPublicKey[:])},
|
||||
}
|
||||
|
||||
salt := make([]byte, 0, 32*2)
|
||||
salt = append(salt, ourPublicKey[:]...)
|
||||
salt = append(salt, r.theirPublicKey[:]...)
|
||||
h := hkdf.New(sha256.New, sharedSecret[:], salt, []byte(ed25519Label))
|
||||
salt := make([]byte, 0, len(ourPublicKey)+len(r.theirPublicKey))
|
||||
salt = append(salt, ourPublicKey...)
|
||||
salt = append(salt, r.theirPublicKey...)
|
||||
h := hkdf.New(sha256.New, sharedSecret, salt, []byte(ed25519Label))
|
||||
wrappingKey := make([]byte, chacha20poly1305.KeySize)
|
||||
if _, err := io.ReadFull(h, wrappingKey); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -256,7 +249,7 @@ func (r *SSHEd25519Recipient) Wrap(fileKey []byte) (*format.Recipient, error) {
|
|||
}
|
||||
|
||||
type SSHEd25519Identity struct {
|
||||
secretKey, ourPublicKey [32]byte
|
||||
secretKey, ourPublicKey []byte
|
||||
sshKey ssh.PublicKey
|
||||
}
|
||||
|
||||
|
|
@ -270,11 +263,10 @@ func NewSSHEd25519Identity(key ed25519.PrivateKey) (*SSHEd25519Identity, error)
|
|||
return nil, err
|
||||
}
|
||||
i := &SSHEd25519Identity{
|
||||
sshKey: s.PublicKey(),
|
||||
sshKey: s.PublicKey(),
|
||||
secretKey: ed25519PrivateKeyToCurve25519(key),
|
||||
}
|
||||
secretKey := ed25519PrivateKeyToCurve25519(key)
|
||||
copy(i.secretKey[:], secretKey)
|
||||
curve25519.ScalarBaseMult(&i.ourPublicKey, &i.secretKey)
|
||||
i.ourPublicKey, _ = curve25519.X25519(i.secretKey, curve25519.Basepoint)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
|
|
@ -296,54 +288,46 @@ func ParseSSHIdentity(pemBytes []byte) (Identity, error) {
|
|||
|
||||
func ed25519PrivateKeyToCurve25519(pk ed25519.PrivateKey) []byte {
|
||||
h := sha512.New()
|
||||
h.Write(pk[:32])
|
||||
h.Write(pk.Seed())
|
||||
out := h.Sum(nil)
|
||||
return out[:32]
|
||||
return out[:curve25519.ScalarSize]
|
||||
}
|
||||
|
||||
func (i *SSHEd25519Identity) Unwrap(block *format.Recipient) ([]byte, error) {
|
||||
// TODO: DRY this up with the X25519 implementation.
|
||||
if block.Type != "ssh-ed25519" {
|
||||
return nil, ErrIncorrectIdentity
|
||||
}
|
||||
if len(block.Args) != 2 {
|
||||
return nil, errors.New("invalid ssh-ed25519 recipient block")
|
||||
}
|
||||
hash, err := format.DecodeString(block.Args[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse ssh-ed25519 recipient: %v", err)
|
||||
}
|
||||
if len(hash) != 4 {
|
||||
return nil, errors.New("invalid ssh-ed25519 recipient block")
|
||||
}
|
||||
publicKey, err := format.DecodeString(block.Args[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse ssh-ed25519 recipient: %v", err)
|
||||
}
|
||||
if len(publicKey) != 32 {
|
||||
if len(publicKey) != curve25519.PointSize {
|
||||
return nil, errors.New("invalid ssh-ed25519 recipient block")
|
||||
}
|
||||
|
||||
sH := sha256.New()
|
||||
sH.Write(i.sshKey.Marshal())
|
||||
hh := sH.Sum(nil)
|
||||
if !bytes.Equal(hh[:4], hash) {
|
||||
if block.Args[0] != SSHFingerprint(i.sshKey) {
|
||||
return nil, ErrIncorrectIdentity
|
||||
}
|
||||
|
||||
var sharedSecret, theirPublicKey, tweak [32]byte
|
||||
copy(theirPublicKey[:], publicKey)
|
||||
sharedSecret, err := curve25519.X25519(i.secretKey, publicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid X25519 recipient: %v", err)
|
||||
}
|
||||
|
||||
tweak := make([]byte, curve25519.ScalarSize)
|
||||
tH := hkdf.New(sha256.New, nil, i.sshKey.Marshal(), []byte(ed25519Label))
|
||||
if _, err := io.ReadFull(tH, tweak[:]); err != nil {
|
||||
if _, err := io.ReadFull(tH, tweak); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
curve25519.ScalarMult(&sharedSecret, &i.secretKey, &theirPublicKey)
|
||||
curve25519.ScalarMult(&sharedSecret, &tweak, &sharedSecret)
|
||||
sharedSecret, _ = curve25519.X25519(tweak, sharedSecret)
|
||||
|
||||
salt := make([]byte, 0, 32*2)
|
||||
salt = append(salt, theirPublicKey[:]...)
|
||||
salt = append(salt, i.ourPublicKey[:]...)
|
||||
h := hkdf.New(sha256.New, sharedSecret[:], salt, []byte(ed25519Label))
|
||||
salt := make([]byte, 0, len(publicKey)+len(i.ourPublicKey))
|
||||
salt = append(salt, publicKey...)
|
||||
salt = append(salt, i.ourPublicKey...)
|
||||
h := hkdf.New(sha256.New, sharedSecret, salt, []byte(ed25519Label))
|
||||
wrappingKey := make([]byte, chacha20poly1305.KeySize)
|
||||
if _, err := io.ReadFull(h, wrappingKey); err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ import (
|
|||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/FiloSottile/age/internal/curve25519"
|
||||
"github.com/FiloSottile/age/internal/format"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ func NewX25519Identity(secretKey []byte) (*X25519Identity, error) {
|
|||
}
|
||||
|
||||
func GenerateX25519Identity() (*X25519Identity, error) {
|
||||
secretKey := make([]byte, 32)
|
||||
secretKey := make([]byte, curve25519.ScalarSize)
|
||||
if _, err := rand.Read(secretKey); err != nil {
|
||||
return nil, fmt.Errorf("internal error: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
// Package curve25519 implements the new proposed API for
|
||||
// golang.org/x/crypto/curve25519 from golang.org/issue/32670.
|
||||
package curve25519
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
const (
|
||||
// ScalarSize is the size of the scalar input to X25519.
|
||||
ScalarSize = 32
|
||||
// PointSize is the size of the point input to X25519.
|
||||
PointSize = 32
|
||||
)
|
||||
|
||||
// Basepoint is the canonical Curve25519 generator.
|
||||
var Basepoint []byte
|
||||
|
||||
func init() {
|
||||
Basepoint = []byte{
|
||||
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
}
|
||||
|
||||
func checkBasepoint() {
|
||||
if subtle.ConstantTimeCompare(Basepoint, []byte{
|
||||
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}) != 1 {
|
||||
panic("curve25519: global Basepoint value was modified")
|
||||
}
|
||||
}
|
||||
|
||||
// X25519 returns the result of the scalar multiplication (scalar * point),
|
||||
// according to RFC 7748, Section 5. scalar, point and the return value are
|
||||
// slices of 32 bytes.
|
||||
//
|
||||
// scalar can be egenrated at random, for example with crypto/rand. point should
|
||||
// be either Basepoint or the output of an X25519 call.
|
||||
//
|
||||
// If point is Basepoint (but not if it's a different slice with the same
|
||||
// contents) a precomputed implementation might be used for performance.
|
||||
func X25519(scalar, point []byte) ([]byte, error) {
|
||||
// Outline the body of function, to let the allocation be inlined in the
|
||||
// caller, and possibly avoid escaping to the heap.
|
||||
var dst [32]byte
|
||||
return x25519(&dst, scalar, point)
|
||||
}
|
||||
|
||||
func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) {
|
||||
var in [32]byte
|
||||
if l := len(scalar); l != 32 {
|
||||
return nil, fmt.Errorf("bad scalar length: %d, expected %d", l, 32)
|
||||
}
|
||||
if l := len(point); l != 32 {
|
||||
return nil, fmt.Errorf("bad point length: %d, expected %d", l, 32)
|
||||
}
|
||||
copy(in[:], scalar)
|
||||
if &point[0] == &Basepoint[0] {
|
||||
checkBasepoint()
|
||||
curve25519.ScalarBaseMult(dst, &in)
|
||||
} else {
|
||||
var base, zero [32]byte
|
||||
copy(base[:], point)
|
||||
curve25519.ScalarMult(dst, &in, &base)
|
||||
if subtle.ConstantTimeCompare(dst[:], zero[:]) == 1 {
|
||||
// TODO: test this codepath with all low order points.
|
||||
return nil, fmt.Errorf("bad input point: low order point")
|
||||
}
|
||||
}
|
||||
return dst[:], nil
|
||||
}
|
||||
Loading…
Reference in a new issue