Commit 7e5bc477 authored by bill_ofarrell's avatar bill_ofarrell Committed by Michael Munday

crypto/ecdsa: implement ecdsa on s390x for P256/P384/P521 using KDSA instruction

Utilize KDSA when available. This guarantees constant time operation on all three curves mentioned,
and is faster than conventional assembly. The IBM Z model(s) that support KDSA as used in this CL
are not yet publicly available, and so we are unable to release performance data at this time.

Change-Id: I85360dcf90fe42d2bf32afe3f638e282de10a518
Reviewed-on: https://go-review.googlesource.com/c/go/+/174437
Run-TryBot: Michael Munday <mike.munday@ibm.com>
Reviewed-by: default avatarMichael Munday <mike.munday@ibm.com>
parent b84e0bc6
......@@ -21,13 +21,12 @@ import (
"crypto/aes"
"crypto/cipher"
"crypto/elliptic"
"crypto/internal/randutil"
"crypto/sha512"
"encoding/asn1"
"errors"
"io"
"math/big"
"crypto/internal/randutil"
)
// A invertible implements fast inverse mod Curve.Params().N
......@@ -190,14 +189,21 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
// See [NSA] 3.4.1
c := priv.PublicKey.Curve
e := hashToInt(hash, c)
r, s, err = sign(priv, &csprng, c, e)
return
}
func signGeneric(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, e *big.Int) (r, s *big.Int, err error) {
N := c.Params().N
if N.Sign() == 0 {
return nil, nil, errZeroParam
}
var k, kInv *big.Int
for {
for {
k, err = randFieldElement(c, csprng)
k, err = randFieldElement(c, *csprng)
if err != nil {
r = nil
return
......@@ -215,8 +221,6 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
break
}
}
e := hashToInt(hash, c)
s = new(big.Int).Mul(priv.D, r)
s.Add(s, e)
s.Mul(s, kInv)
......@@ -225,7 +229,6 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
break
}
}
return
}
......@@ -243,8 +246,12 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
return false
}
e := hashToInt(hash, c)
return verify(pub, c, e, r, s)
}
func verifyGeneric(pub *PublicKey, c elliptic.Curve, e, r, s *big.Int) bool {
var w *big.Int
N := c.Params().N
if in, ok := c.(invertible); ok {
w = in.Inverse(s)
} else {
......
// Copyright 2019 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
package ecdsa
import (
"crypto/cipher"
"crypto/elliptic"
"math/big"
)
func sign(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, e *big.Int) (r, s *big.Int, err error) {
r, s, err = signGeneric(priv, csprng, c, e)
return
}
func verify(pub *PublicKey, c elliptic.Curve, e, r, s *big.Int) bool {
return verifyGeneric(pub, c, e, r, s)
}
// Copyright 2019 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,!gccgo
package ecdsa
import (
"crypto/cipher"
"crypto/elliptic"
"internal/cpu"
"math/big"
)
// s390x accelerated signatures
//go:noescape
func kdsaSig(fc uint64, block *[1720]byte) (errn uint64)
type signverify int
const (
signing signverify = iota
verifying
)
// bufferOffsets represents the offset of a particular parameter in
// the buffer passed to the KDSA instruction.
type bufferOffsets struct {
baseSize int
hashSize int
offsetHash int
offsetKey1 int
offsetRNorKey2 int
offsetR int
offsetS int
functionCode uint64
}
func canUseKDSA(sv signverify, c elliptic.Curve, bo *bufferOffsets) bool {
if !cpu.S390X.HasECDSA {
return false
}
switch c.Params().Name {
case "P-256":
bo.baseSize = 32
bo.hashSize = 32
bo.offsetHash = 64
bo.offsetKey1 = 96
bo.offsetRNorKey2 = 128
bo.offsetR = 0
bo.offsetS = 32
if sv == signing {
bo.functionCode = 137
} else {
bo.functionCode = 1
}
return true
case "P-384":
bo.baseSize = 48
bo.hashSize = 48
bo.offsetHash = 96
bo.offsetKey1 = 144
bo.offsetRNorKey2 = 192
bo.offsetR = 0
bo.offsetS = 48
if sv == signing {
bo.functionCode = 138
} else {
bo.functionCode = 2
}
return true
case "P-521":
bo.baseSize = 66
bo.hashSize = 80
bo.offsetHash = 160
bo.offsetKey1 = 254
bo.offsetRNorKey2 = 334
bo.offsetR = 14
bo.offsetS = 94
if sv == signing {
bo.functionCode = 139
} else {
bo.functionCode = 3
}
return true
}
return false
}
// zeroExtendAndCopy pads src with leading zeros until it has the size given.
// It then copies the padded src into the dst. Bytes beyond size in dst are
// not modified.
func zeroExtendAndCopy(dst, src []byte, size int) {
nz := size - len(src)
if nz < 0 {
panic("src is too long")
}
// the compiler should replace this loop with a memclr call
z := dst[:nz]
for i := range z {
z[i] = 0
}
copy(dst[nz:size], src[:size-nz])
return
}
func sign(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, e *big.Int) (r, s *big.Int, err error) {
var bo bufferOffsets
if canUseKDSA(signing, c, &bo) && e.Sign() != 0 {
var buffer [1720]byte
for {
var k *big.Int
k, err = randFieldElement(c, csprng)
if err != nil {
return nil, nil, err
}
zeroExtendAndCopy(buffer[bo.offsetHash:], e.Bytes(), bo.hashSize)
zeroExtendAndCopy(buffer[bo.offsetKey1:], priv.D.Bytes(), bo.baseSize)
zeroExtendAndCopy(buffer[bo.offsetRNorKey2:], k.Bytes(), bo.baseSize)
errn := kdsaSig(bo.functionCode, &buffer)
if errn == 2 {
return nil, nil, errZeroParam
}
if errn == 0 { // success == 0 means successful signing
r = new(big.Int)
r.SetBytes(buffer[bo.offsetR : bo.offsetR+bo.baseSize])
s = new(big.Int)
s.SetBytes(buffer[bo.offsetS : bo.offsetS+bo.baseSize])
return
}
//at this point, it must be that errn == 1: retry
}
}
r, s, err = signGeneric(priv, csprng, c, e)
return
}
func verify(pub *PublicKey, c elliptic.Curve, e, r, s *big.Int) bool {
var bo bufferOffsets
if canUseKDSA(verifying, c, &bo) && e.Sign() != 0 {
var buffer [1720]byte
zeroExtendAndCopy(buffer[bo.offsetR:], r.Bytes(), bo.baseSize)
zeroExtendAndCopy(buffer[bo.offsetS:], s.Bytes(), bo.baseSize)
zeroExtendAndCopy(buffer[bo.offsetHash:], e.Bytes(), bo.hashSize)
zeroExtendAndCopy(buffer[bo.offsetKey1:], pub.X.Bytes(), bo.baseSize)
zeroExtendAndCopy(buffer[bo.offsetRNorKey2:], pub.Y.Bytes(), bo.baseSize)
errn := kdsaSig(bo.functionCode, &buffer)
return errn == 0
}
return verifyGeneric(pub, c, e, r, s)
}
// Copyright 2019 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.
#include "textflag.h"
// func kdsaSig(fc uint64, block *[1720]byte) (errn uint64)
TEXT ·kdsaSig(SB), NOSPLIT|NOFRAME, $0-24
MOVD fc+0(FP), R0 // function code
MOVD block+8(FP), R1 // address parameter block
loop:
WORD $0xB93A0008 // compute digital signature authentication
BVS loop // branch back if interrupted
BEQ success // signature creation successful
BGT retry // signing unsuccessful, but retry with new CSPRN
error:
MOVD $2, R2 // fallthrough indicates fatal error
MOVD R2, errn+16(FP) // return 2 - sign/verify abort
RET
retry:
MOVD $1, R2
MOVD R2, errn+16(FP) // return 1 - sign/verify was unsuccessful -- if sign, retry with new RN
RET
success:
MOVD $0, R2
MOVD R2, errn+16(FP) // return 0 - sign/verify was successful
RET
// Copyright 2019 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,!gccgo
package ecdsa
import (
"crypto/elliptic"
"testing"
)
func TestNoAsm(t *testing.T) {
curves := [...]elliptic.Curve{
elliptic.P256(),
elliptic.P384(),
elliptic.P521(),
}
for _, curve := range curves {
// override the name of the curve to stop the assembly path being taken
params := *curve.Params()
name := params.Name
params.Name = name + "_GENERIC_OVERRIDE"
testKeyGeneration(t, &params, name)
testSignAndVerify(t, &params, name)
testNonceSafety(t, &params, name)
testINDCCA(t, &params, name)
testNegativeInputs(t, &params, name)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment