mirror of
https://github.com/golang/go
synced 2024-11-18 13:44:48 -07:00
crypto/ed25519: implement ed25519 on s390x using KDSA instruction
This CL allows the usage of KDSA instruction when it is available. The instruction is designed to be resistant to side channel attacks and offers performance improvement for ed25519. Benchmarks: name old time/op new time/op delta Signing-8 120µs ±20% 62µs ±12% -48.40% (p=0.000 n=10+10) Verification-8 325µs ±17% 69µs ±10% -78.80% (p=0.000 n=10+10) name old alloc/op new alloc/op delta Signing-8 448B ± 0% 0B -100.00% (p=0.000 n=10+10) Verification-8 288B ± 0% 0B -100.00% (p=0.000 n=10+10) name old allocs/op new allocs/op delta Signing-8 5.00 ± 0% 0.00 -100.00% (p=0.000 n=10+10) Verification-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=10+10) Change-Id: I0330ce83d807370b419ce638bc2cae4cb3c250dc Reviewed-on: https://go-review.googlesource.com/c/go/+/202578 Run-TryBot: Michael Munday <mike.munday@ibm.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Michael Munday <mike.munday@ibm.com>
This commit is contained in:
parent
6a4441d6fe
commit
d646c035f9
@ -142,7 +142,7 @@ func Sign(privateKey PrivateKey, message []byte) []byte {
|
||||
return signature
|
||||
}
|
||||
|
||||
func sign(signature, privateKey, message []byte) {
|
||||
func signGeneric(signature, privateKey, message []byte) {
|
||||
if l := len(privateKey); l != PrivateKeySize {
|
||||
panic("ed25519: bad private key length: " + strconv.Itoa(l))
|
||||
}
|
||||
@ -189,6 +189,10 @@ func sign(signature, privateKey, message []byte) {
|
||||
// Verify reports whether sig is a valid signature of message by publicKey. It
|
||||
// will panic if len(publicKey) is not PublicKeySize.
|
||||
func Verify(publicKey PublicKey, message, sig []byte) bool {
|
||||
return verify(publicKey, message, sig)
|
||||
}
|
||||
|
||||
func verifyGeneric(publicKey PublicKey, message, sig []byte) bool {
|
||||
if l := len(publicKey); l != PublicKeySize {
|
||||
panic("ed25519: bad public key length: " + strconv.Itoa(l))
|
||||
}
|
||||
|
15
src/crypto/ed25519/ed25519_noasm.go
Normal file
15
src/crypto/ed25519/ed25519_noasm.go
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !s390x purego
|
||||
|
||||
package ed25519
|
||||
|
||||
func sign(signature, privateKey, message []byte) {
|
||||
signGeneric(signature, privateKey, message)
|
||||
}
|
||||
|
||||
func verify(publicKey PublicKey, message, sig []byte) bool {
|
||||
return verifyGeneric(publicKey, message, sig)
|
||||
}
|
53
src/crypto/ed25519/ed25519_s390x.go
Normal file
53
src/crypto/ed25519/ed25519_s390x.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !purego
|
||||
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
"internal/cpu"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//go:noescape
|
||||
func kdsaSign(message, signature, privateKey []byte) bool
|
||||
|
||||
//go:noescape
|
||||
func kdsaVerify(message, signature, publicKey []byte) bool
|
||||
|
||||
// sign does a check to see if hardware has Edwards Curve instruction available.
|
||||
// If it does, use the hardware implementation. Otherwise, use the generic version.
|
||||
func sign(signature, privateKey, message []byte) {
|
||||
if cpu.S390X.HasEDDSA {
|
||||
if l := len(privateKey); l != PrivateKeySize {
|
||||
panic("ed25519: bad private key length: " + strconv.Itoa(l))
|
||||
}
|
||||
|
||||
ret := kdsaSign(message, signature, privateKey[:32])
|
||||
if !ret {
|
||||
panic("ed25519: kdsa sign has a failure")
|
||||
}
|
||||
return
|
||||
}
|
||||
signGeneric(signature, privateKey, message)
|
||||
}
|
||||
|
||||
// verify does a check to see if hardware has Edwards Curve instruction available.
|
||||
// If it does, use the hardware implementation for eddsa verfication. Otherwise, the generic
|
||||
// version is used
|
||||
func verify(publicKey PublicKey, message, sig []byte) bool {
|
||||
if cpu.S390X.HasEDDSA {
|
||||
if l := len(publicKey); l != PublicKeySize {
|
||||
panic("ed25519: bad public key length: " + strconv.Itoa(l))
|
||||
}
|
||||
|
||||
if len(sig) != SignatureSize || sig[63]&224 != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return kdsaVerify(message, sig, publicKey)
|
||||
}
|
||||
return verifyGeneric(publicKey, message, sig)
|
||||
}
|
163
src/crypto/ed25519/ed25519_s390x.s
Normal file
163
src/crypto/ed25519/ed25519_s390x.s
Normal file
@ -0,0 +1,163 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func kdsaSign(message, signature, privateKey []byte) bool
|
||||
TEXT ·kdsaSign(SB), $4096-73
|
||||
// The kdsa instruction takes function code,
|
||||
// buffer's location, message's location and message len
|
||||
// as parameters. Out of those, the function code and buffer's location
|
||||
// should be placed in R0 and R1 respectively. The message's location
|
||||
// and message length should be placed in an even-odd register pair. (e.g: R2 and R3)
|
||||
|
||||
// The content of parameter block(buffer) looks like the following:
|
||||
// Signature R, Signature S and Private Key all take 32 bytes.
|
||||
// In the signing case, the signatures(R and S) will be generated by
|
||||
// the signing instruction and get placed in the locations shown in the parameter block.
|
||||
// 0 +---------------+
|
||||
// | Signature(R) |
|
||||
// 32 +---------------+
|
||||
// | Signature(S) |
|
||||
// 64 +---------------+
|
||||
// | Private Key |
|
||||
// 96 +---------------+
|
||||
// | Reserved |
|
||||
// 112+---------------+
|
||||
// | |
|
||||
// | ... |
|
||||
// | |
|
||||
// 4088 +---------------+
|
||||
|
||||
// The following code section setups the buffer from stack:
|
||||
// Get the address of the buffer stack variable.
|
||||
MOVD $buffer-4096(SP), R1
|
||||
|
||||
// Zero the buffer.
|
||||
MOVD R1, R2
|
||||
MOVD $(4096/256), R0 // number of 256 byte chunks to clear
|
||||
|
||||
clear:
|
||||
XC $256, (R2), (R2)
|
||||
MOVD $256(R2), R2
|
||||
BRCTG R0, clear
|
||||
|
||||
MOVD $40, R0 // EDDSA-25519 sign has a function code of 40
|
||||
LMG message+0(FP), R2, R3 // R2=base R3=len
|
||||
LMG signature+24(FP), R4, R5 // R4=base R5=len
|
||||
LMG privateKey+48(FP), R6, R7 // R6=base R7=len
|
||||
|
||||
// Checks the length of signature and private key
|
||||
CMPBNE R5, $64, panic
|
||||
CMPBNE R7, $32, panic
|
||||
|
||||
// The instruction uses RFC 8032's private key, which is the first 32 bytes
|
||||
// of the private key in this package. So we copy that into the buffer.
|
||||
MVC $32, (R6), 64(R1)
|
||||
|
||||
loop:
|
||||
WORD $0xB93A0002 // The KDSA instruction
|
||||
BVS loop // The instruction is exectued by hardware and can be interrupted. This does a retry when that happens.
|
||||
BNE error
|
||||
|
||||
success:
|
||||
// The signatures generated are in big-endian form, so we
|
||||
// need to reverse the bytes of Signature(R) and Signature(S) in the buffers to transform
|
||||
// them from big-endian to little-endian.
|
||||
|
||||
// Transform Signature(R) from big endian to little endian and copy into the signature
|
||||
MVCIN $32, 31(R1), (R4)
|
||||
|
||||
// Transform Signature(S) from big endian to little endian and copy into the signature
|
||||
MVCIN $32, 63(R1), 32(R4)
|
||||
|
||||
MOVB $1, ret+72(FP)
|
||||
RET
|
||||
|
||||
error:
|
||||
// return false
|
||||
MOVB $0, ret+72(FP)
|
||||
RET
|
||||
|
||||
panic:
|
||||
UNDEF
|
||||
|
||||
// func kdsaVerify(message, signature, publicKey []byte) bool
|
||||
TEXT ·kdsaVerify(SB), $4096-73
|
||||
// The kdsa instruction takes function code,
|
||||
// buffer's location, message's location and message len
|
||||
// as parameters. Out of those, the function code and buffer's location
|
||||
// should be placed in R0 and R1 respectively. The message's location
|
||||
// and message length should be placed in an even-odd register pair. (e.g: R2 and R3)
|
||||
|
||||
// The parameter block(buffer) is similar to that of signing, except that
|
||||
// we use public key for verification, and Signatures(R and S) are provided
|
||||
// as input parameters to the parameter block.
|
||||
// 0 +---------------+
|
||||
// | Signature(R) |
|
||||
// 32 +---------------+
|
||||
// | Signature(S) |
|
||||
// 64 +---------------+
|
||||
// | Public Key |
|
||||
// 96 +---------------+
|
||||
// | Reserved |
|
||||
// 112+---------------+
|
||||
// | |
|
||||
// | ... |
|
||||
// | |
|
||||
// 4088 +---------------+
|
||||
|
||||
// The following code section setups the buffer from stack:
|
||||
// Get the address of the buffer stack variable.
|
||||
MOVD $buffer-4096(SP), R1
|
||||
|
||||
// Zero the buffer.
|
||||
MOVD R1, R2
|
||||
MOVD $(4096/256), R0 // number of 256 byte chunks to clear
|
||||
|
||||
clear:
|
||||
XC $256, (R2), (R2)
|
||||
MOVD $256(R2), R2
|
||||
BRCTG R0, clear
|
||||
|
||||
MOVD $32, R0 // EDDSA-25519 verify has a function code of 32
|
||||
LMG message+0(FP), R2, R3 // R2=base R3=len
|
||||
LMG signature+24(FP), R4, R5 // R4=base R5=len
|
||||
LMG publicKey+48(FP), R6, R7 // R6=base R7=len
|
||||
|
||||
// Checks the length of public key and signature
|
||||
CMPBNE R5, $64, panic
|
||||
CMPBNE R7, $32, panic
|
||||
|
||||
verify:
|
||||
// The instruction needs Signature(R), Signature(S) and public key
|
||||
// to be in big-endian form during computation. Therefore,
|
||||
// we do the transformation (from little endian to big endian) and copy those into the buffer.
|
||||
|
||||
// Transform Signature(R) from little endian to big endian and copy into the buffer
|
||||
MVCIN $32, 31(R4), (R1)
|
||||
|
||||
// Transform Signature(S) from little endian to big endian and copy into the buffer
|
||||
MVCIN $32, 63(R4), 32(R1)
|
||||
|
||||
// Transform Public Key from little endian to big endian and copy into the buffer
|
||||
MVCIN $32, 31(R6), 64(R1)
|
||||
|
||||
verifyLoop:
|
||||
WORD $0xB93A0002 // KDSA instruction
|
||||
BVS verifyLoop // Retry upon hardware interrupt
|
||||
BNE error
|
||||
|
||||
success:
|
||||
MOVB $1, ret+72(FP)
|
||||
RET
|
||||
|
||||
error:
|
||||
MOVB $0, ret+72(FP)
|
||||
RET
|
||||
|
||||
panic:
|
||||
UNDEF
|
@ -26,6 +26,14 @@ func (zeroReader) Read(buf []byte) (int, error) {
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
// signGenericWrapper is identical to Sign except that it unconditionally calls signGeneric directly
|
||||
// rather than going through the sign function that might call assembly code.
|
||||
func signGenericWrapper(privateKey PrivateKey, msg []byte) []byte {
|
||||
sig := make([]byte, SignatureSize)
|
||||
signGeneric(sig, privateKey, msg)
|
||||
return sig
|
||||
}
|
||||
|
||||
func TestUnmarshalMarshal(t *testing.T) {
|
||||
pub, _, _ := GenerateKey(rand.Reader)
|
||||
|
||||
@ -45,22 +53,33 @@ func TestUnmarshalMarshal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSignVerify(t *testing.T) {
|
||||
t.Run("Generic", func(t *testing.T) { testSignVerify(t, signGenericWrapper, verifyGeneric) })
|
||||
t.Run("Native", func(t *testing.T) { testSignVerify(t, Sign, Verify) })
|
||||
}
|
||||
|
||||
func testSignVerify(t *testing.T, signImpl func(privateKey PrivateKey, message []byte) []byte,
|
||||
verifyImpl func(publicKey PublicKey, message, sig []byte) bool) {
|
||||
var zero zeroReader
|
||||
public, private, _ := GenerateKey(zero)
|
||||
|
||||
message := []byte("test message")
|
||||
sig := Sign(private, message)
|
||||
if !Verify(public, message, sig) {
|
||||
sig := signImpl(private, message)
|
||||
if !verifyImpl(public, message, sig) {
|
||||
t.Errorf("valid signature rejected")
|
||||
}
|
||||
|
||||
wrongMessage := []byte("wrong message")
|
||||
if Verify(public, wrongMessage, sig) {
|
||||
if verifyImpl(public, wrongMessage, sig) {
|
||||
t.Errorf("signature of different message accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCryptoSigner(t *testing.T) {
|
||||
t.Run("Generic", func(t *testing.T) { testCryptoSigner(t, verifyGeneric) })
|
||||
t.Run("Native", func(t *testing.T) { testCryptoSigner(t, Verify) })
|
||||
}
|
||||
|
||||
func testCryptoSigner(t *testing.T, verifyImpl func(publicKey PublicKey, message, sig []byte) bool) {
|
||||
var zero zeroReader
|
||||
public, private, _ := GenerateKey(zero)
|
||||
|
||||
@ -83,7 +102,7 @@ func TestCryptoSigner(t *testing.T) {
|
||||
t.Fatalf("error from Sign(): %s", err)
|
||||
}
|
||||
|
||||
if !Verify(public, message, signature) {
|
||||
if !verifyImpl(public, message, signature) {
|
||||
t.Errorf("Verify failed on signature from Sign()")
|
||||
}
|
||||
}
|
||||
@ -105,6 +124,12 @@ func TestEqual(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGolden(t *testing.T) {
|
||||
t.Run("Generic", func(t *testing.T) { testGolden(t, signGenericWrapper, verifyGeneric) })
|
||||
t.Run("Native", func(t *testing.T) { testGolden(t, Sign, Verify) })
|
||||
}
|
||||
|
||||
func testGolden(t *testing.T, signImpl func(privateKey PrivateKey, message []byte) []byte,
|
||||
verifyImpl func(publicKey PublicKey, message, sig []byte) bool) {
|
||||
// sign.input.gz is a selection of test cases from
|
||||
// https://ed25519.cr.yp.to/python/sign.input
|
||||
testDataZ, err := os.Open("testdata/sign.input.gz")
|
||||
@ -146,12 +171,12 @@ func TestGolden(t *testing.T) {
|
||||
copy(priv[:], privBytes)
|
||||
copy(priv[32:], pubKey)
|
||||
|
||||
sig2 := Sign(priv[:], msg)
|
||||
sig2 := signImpl(priv[:], msg)
|
||||
if !bytes.Equal(sig, sig2[:]) {
|
||||
t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2)
|
||||
}
|
||||
|
||||
if !Verify(pubKey, msg, sig2) {
|
||||
if !verifyImpl(pubKey, msg, sig2) {
|
||||
t.Errorf("signature failed to verify on line %d", lineNo)
|
||||
}
|
||||
|
||||
@ -175,6 +200,11 @@ func TestGolden(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMalleability(t *testing.T) {
|
||||
t.Run("Generic", func(t *testing.T) { testMalleability(t, verifyGeneric) })
|
||||
t.Run("Native", func(t *testing.T) { testMalleability(t, Verify) })
|
||||
}
|
||||
|
||||
func testMalleability(t *testing.T, verifyImpl func(publicKey PublicKey, message, sig []byte) bool) {
|
||||
// https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test
|
||||
// that s be in [0, order). This prevents someone from adding a multiple of
|
||||
// order to s and obtaining a second valid signature for the same message.
|
||||
@ -193,7 +223,7 @@ func TestMalleability(t *testing.T) {
|
||||
0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa,
|
||||
}
|
||||
|
||||
if Verify(publicKey, msg, sig) {
|
||||
if verifyImpl(publicKey, msg, sig) {
|
||||
t.Fatal("non-canonical signature accepted")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user