1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build !purego
6
7package ecdsa
8
9import (
10	"crypto/elliptic"
11	"errors"
12	"internal/cpu"
13	"io"
14	"math/big"
15)
16
17// kdsa invokes the "compute digital signature authentication"
18// instruction with the given function code and 4096 byte
19// parameter block.
20//
21// The return value corresponds to the condition code set by the
22// instruction. Interrupted invocations are handled by the
23// function.
24//
25//go:noescape
26func kdsa(fc uint64, params *[4096]byte) (errn uint64)
27
28// testingDisableKDSA forces the generic fallback path. It must only be set in tests.
29var testingDisableKDSA bool
30
31// canUseKDSA checks if KDSA instruction is available, and if it is, it checks
32// the name of the curve to see if it matches the curves supported(P-256, P-384, P-521).
33// Then, based on the curve name, a function code and a block size will be assigned.
34// If KDSA instruction is not available or if the curve is not supported, canUseKDSA
35// will set ok to false.
36func canUseKDSA(c elliptic.Curve) (functionCode uint64, blockSize int, ok bool) {
37	if testingDisableKDSA {
38		return 0, 0, false
39	}
40	if !cpu.S390X.HasECDSA {
41		return 0, 0, false
42	}
43	switch c.Params().Name {
44	case "P-256":
45		return 1, 32, true
46	case "P-384":
47		return 2, 48, true
48	case "P-521":
49		return 3, 80, true
50	}
51	return 0, 0, false // A mismatch
52}
53
54func hashToBytes(dst, hash []byte, c elliptic.Curve) {
55	l := len(dst)
56	if n := c.Params().N.BitLen(); n == l*8 {
57		// allocation free path for curves with a length that is a whole number of bytes
58		if len(hash) >= l {
59			// truncate hash
60			copy(dst, hash[:l])
61			return
62		}
63		// pad hash with leading zeros
64		p := l - len(hash)
65		for i := 0; i < p; i++ {
66			dst[i] = 0
67		}
68		copy(dst[p:], hash)
69		return
70	}
71	// TODO(mundaym): avoid hashToInt call here
72	hashToInt(hash, c).FillBytes(dst)
73}
74
75func signAsm(priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, err error) {
76	c := priv.Curve
77	functionCode, blockSize, ok := canUseKDSA(c)
78	if !ok {
79		return nil, errNoAsm
80	}
81	for {
82		var k *big.Int
83		k, err = randFieldElement(c, csprng)
84		if err != nil {
85			return nil, err
86		}
87
88		// The parameter block looks like the following for sign.
89		// 	+---------------------+
90		// 	|   Signature(R)      |
91		//	+---------------------+
92		//	|   Signature(S)      |
93		//	+---------------------+
94		//	|   Hashed Message    |
95		//	+---------------------+
96		//	|   Private Key       |
97		//	+---------------------+
98		//	|   Random Number     |
99		//	+---------------------+
100		//	|                     |
101		//	|        ...          |
102		//	|                     |
103		//	+---------------------+
104		// The common components(signatureR, signatureS, hashedMessage, privateKey and
105		// random number) each takes block size of bytes. The block size is different for
106		// different curves and is set by canUseKDSA function.
107		var params [4096]byte
108
109		// Copy content into the parameter block. In the sign case,
110		// we copy hashed message, private key and random number into
111		// the parameter block.
112		hashToBytes(params[2*blockSize:3*blockSize], hash, c)
113		priv.D.FillBytes(params[3*blockSize : 4*blockSize])
114		k.FillBytes(params[4*blockSize : 5*blockSize])
115		// Convert verify function code into a sign function code by adding 8.
116		// We also need to set the 'deterministic' bit in the function code, by
117		// adding 128, in order to stop the instruction using its own random number
118		// generator in addition to the random number we supply.
119		switch kdsa(functionCode+136, &params) {
120		case 0: // success
121			return encodeSignature(params[:blockSize], params[blockSize:2*blockSize])
122		case 1: // error
123			return nil, errZeroParam
124		case 2: // retry
125			continue
126		}
127		panic("unreachable")
128	}
129}
130
131func verifyAsm(pub *PublicKey, hash []byte, sig []byte) error {
132	c := pub.Curve
133	functionCode, blockSize, ok := canUseKDSA(c)
134	if !ok {
135		return errNoAsm
136	}
137
138	r, s, err := parseSignature(sig)
139	if err != nil {
140		return err
141	}
142	if len(r) > blockSize || len(s) > blockSize {
143		return errors.New("invalid signature")
144	}
145
146	// The parameter block looks like the following for verify:
147	// 	+---------------------+
148	// 	|   Signature(R)      |
149	//	+---------------------+
150	//	|   Signature(S)      |
151	//	+---------------------+
152	//	|   Hashed Message    |
153	//	+---------------------+
154	//	|   Public Key X      |
155	//	+---------------------+
156	//	|   Public Key Y      |
157	//	+---------------------+
158	//	|                     |
159	//	|        ...          |
160	//	|                     |
161	//	+---------------------+
162	// The common components(signatureR, signatureS, hashed message, public key X,
163	// and public key Y) each takes block size of bytes. The block size is different for
164	// different curves and is set by canUseKDSA function.
165	var params [4096]byte
166
167	// Copy content into the parameter block. In the verify case,
168	// we copy signature (r), signature(s), hashed message, public key x component,
169	// and public key y component into the parameter block.
170	copy(params[0*blockSize+blockSize-len(r):], r)
171	copy(params[1*blockSize+blockSize-len(s):], s)
172	hashToBytes(params[2*blockSize:3*blockSize], hash, c)
173	pub.X.FillBytes(params[3*blockSize : 4*blockSize])
174	pub.Y.FillBytes(params[4*blockSize : 5*blockSize])
175	if kdsa(functionCode, &params) != 0 {
176		return errors.New("invalid signature")
177	}
178	return nil
179}
180