server/signedcontainer/internal/convert/convert.go (210 lines of code) (raw):
// Package convert contains functions to innitialize Tink keysets from PEM-encoded data.
package convert
// TODO: Remove this package and migrate to the Tink API when they publish it.
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"math/big"
"github.com/tink-crypto/tink-go/v2/keyset"
tinkecdsa "github.com/tink-crypto/tink-go/v2/signature/ecdsa"
"github.com/tink-crypto/tink-go/v2/signature/rsassapkcs1"
"github.com/tink-crypto/tink-go/v2/signature/rsassapss"
)
const (
// RSA default public exponent (aka F4).
f4 = 65537
)
// unmarshalPEMToPublicKey converts a PEM-encoded byte slice into a crypto.PublicKey.
func unmarshalPEMToPublicKey(pemBytes []byte) (crypto.PublicKey, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("no PEM data found, failed to decode PEM-encoded byte slice")
}
switch block.Type {
case "PUBLIC KEY":
return x509.ParsePKIXPublicKey(block.Bytes)
case "RSA PUBLIC KEY":
return x509.ParsePKCS1PublicKey(block.Bytes)
default:
return nil, fmt.Errorf("unsupported public key type: %v", block.Type)
}
}
// createECDSAP256SHA256WithDERNoPrefixPublicKey creates a Tink [tinkecdsa.PublicKey].
//
// The key uses P256 as the curve, SHA256 as the hash function, DER signature
// encoding and does not add an output prefix.
func createECDSAP256SHA256WithDERNoPrefixPublicKey(pubKey crypto.PublicKey) (*tinkecdsa.PublicKey, error) {
ecdsaPubKey, ok := pubKey.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not an ECDSA public key: %v", pubKey)
}
ecdhPubKey, err := ecdsaPubKey.ECDH()
if err != nil {
return nil, err
}
// Turn this into a Tink key.
params, err := tinkecdsa.NewParameters(tinkecdsa.NistP256, tinkecdsa.SHA256, tinkecdsa.DER, tinkecdsa.VariantNoPrefix)
if err != nil {
return nil, err
}
// Will fail if the point is not on the curve.
return tinkecdsa.NewPublicKey(ecdhPubKey.Bytes(), 0, params)
}
// PemToECDSAP256Sha256WithDEREncodingKeysetHandle converts a PEM-encoded byte
// slice into a Tink public Keyset.
//
// ECDSA Signatures need to used the ASN.1 DER encoding.
//
// The JWA RFC for ES256, ES384 and ES512 mandates a different encoding,
// so this generated with this class are not conformant with the JWA
// standard. See https://www.rfc-editor.org/rfc/rfc7518#section-3.4.
func PemToECDSAP256Sha256WithDEREncodingKeysetHandle(pemBytes []byte) (*keyset.Handle, error) {
publicKey, err := unmarshalPEMToPublicKey(pemBytes)
if err != nil {
return nil, err
}
tinkPublicKey, err := createECDSAP256SHA256WithDERNoPrefixPublicKey(publicKey)
if err != nil {
return nil, err
}
km := keyset.NewManager()
keyID, err := km.AddKey(tinkPublicKey)
if err != nil {
return nil, err
}
if err := km.SetPrimary(keyID); err != nil {
return nil, err
}
return km.Handle()
}
// PemToRsaSsaPkcs1Sha256KeysetHandle converts a PEM-encoded byte slice into a Tink public Keyset.
//
// Note that only OID "rsaEncryption" is supported. The OIDs "sha256WithRSAEncryption",
// "sha384WithRSAEncryption" and "sha512WithRSAEncryption" are not supported.
// See RFC 4055 Section 1.2 and Section 5 for a discussion of these OIDs.
func PemToRsaSsaPkcs1Sha256KeysetHandle(pemBytes []byte) (*keyset.Handle, error) {
publicKey, err := unmarshalPEMToPublicKey(pemBytes)
if err != nil {
return nil, err
}
rsaPublicKey, ok := publicKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not a RSA public key: %v", publicKey)
}
// Turn this into a Tink key.
params, err := rsassapkcs1.NewParameters(rsaPublicKey.N.BitLen(), rsassapkcs1.SHA256, f4, rsassapkcs1.VariantNoPrefix)
if err != nil {
return nil, err
}
tinkPublicKey, err := rsassapkcs1.NewPublicKey(rsaPublicKey.N.Bytes(), 0, params)
if err != nil {
return nil, err
}
km := keyset.NewManager()
id, err := km.AddKey(tinkPublicKey)
if err != nil {
return nil, err
}
if err := km.SetPrimary(id); err != nil {
return nil, err
}
return km.Handle()
}
// PemToRsaSsaPssSha256KeysetHandle converts a PEM-encoded byte slice into a Tink public Keyset.
//
// Note that only OID "rsaEncryption" is supported. The OID "id-RSASSA-PSS" is not supported.
// See RFC 4055 Section 1.2 for a discussion of these OIDs.
func PemToRsaSsaPssSha256KeysetHandle(pemBytes []byte) (*keyset.Handle, error) {
publicKey, err := unmarshalPEMToPublicKey(pemBytes)
if err != nil {
return nil, err
}
rsaPublicKey, ok := publicKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not a RSA public key: %v", publicKey)
}
// Turn this into a Tink key.
params, err := rsassapss.NewParameters(rsassapss.ParametersValues{
ModulusSizeBits: rsaPublicKey.N.BitLen(),
SigHashType: rsassapss.SHA256,
MGF1HashType: rsassapss.SHA256,
PublicExponent: rsaPublicKey.E,
SaltLengthBytes: rsa.PSSSaltLengthAuto,
}, rsassapss.VariantNoPrefix)
if err != nil {
return nil, err
}
tinkPublicKey, err := rsassapss.NewPublicKey(rsaPublicKey.N.Bytes(), 0, params)
if err != nil {
return nil, err
}
km := keyset.NewManager()
id, err := km.AddKey(tinkPublicKey)
if err != nil {
return nil, err
}
if err := km.SetPrimary(id); err != nil {
return nil, err
}
return km.Handle()
}
// PemFromECDSAP256Sha256WithDEREncodingKeysetHandle converts a Tink Keyset
// with one EcdsaPublicKey (over curve P-256 using SHA256 and DER signature
// encoding) into a PEM-encoded key.
//
// Note that the PEM encoded key does not have all the metadata the Tink key
// has. This can produce unexpected incompatibilities, see
// https://developers.google.com/tink/design/access_control#accessing_partial_keys
func PemFromECDSAP256Sha256WithDEREncodingKeysetHandle(handle *keyset.Handle) ([]byte, error) {
if handle.Len() != 1 {
return nil, fmt.Errorf("unexpected number of keys: got %v, want 1", handle.Len())
}
entry, err := handle.Entry(0)
if err != nil {
return nil, err
}
if entry.KeyStatus() != keyset.Enabled {
return nil, fmt.Errorf("unsupported key status: %v, want %v", entry.KeyStatus(), keyset.Enabled)
}
publicKey, ok := entry.Key().(*tinkecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("invalid key type: %T, want *tinkecdsa.PublicKey", entry.Key())
}
params := publicKey.Parameters().(*tinkecdsa.Parameters)
if params.HashType() != tinkecdsa.SHA256 {
return nil, fmt.Errorf("unsupported hash type: %v, want %v", params.HashType(), tinkecdsa.SHA256)
}
if params.CurveType() != tinkecdsa.NistP256 {
return nil, fmt.Errorf("unsupported curve type: %v, want %v", params.CurveType(), tinkecdsa.NistP256)
}
if params.SignatureEncoding() != tinkecdsa.DER {
return nil, fmt.Errorf("unsupported signature encoding: %v, want %v", params.SignatureEncoding(), tinkecdsa.DER)
}
if params.Variant() != tinkecdsa.VariantNoPrefix {
return nil, fmt.Errorf("unsupported output prefix variant: %v, want %v", params.Variant(), tinkecdsa.VariantNoPrefix)
}
// publicKey.PublicPoint() is in the uncompressed format as defined in
// SEC 1 v2.0, Section 2.3.3 (https://www.secg.org/sec1-v2.pdf#page=17.08).
x, y := elliptic.Unmarshal(elliptic.P256(), publicKey.PublicPoint())
encoded, err := x509.MarshalPKIXPublicKey(
&ecdsa.PublicKey{
Curve: elliptic.P256(),
X: x,
Y: y,
})
if err != nil {
return nil, fmt.Errorf("x509.MarshalPKIXPublicKey failed: %v", err)
}
block := &pem.Block{
Type: "PUBLIC KEY",
Bytes: encoded,
}
return pem.EncodeToMemory(block), nil
}
// PemFromRsaSsaPkcs1Sha256KeysetHandle converts a Tink Keyset with one RsaSsaPkcs1PublicKey
// (using SHA256) into a PEM-encoded key.
//
// Note that the PEM encoded key does not have all the metadata the Tink key has.
// This can produce unexpected incompatibilities, see
// https://developers.google.com/tink/design/access_control#accessing_partial_keys
func PemFromRsaSsaPkcs1Sha256KeysetHandle(handle *keyset.Handle) ([]byte, error) {
if handle.Len() != 1 {
return nil, fmt.Errorf("unexpected number of keys: got %v, want 1", handle.Len())
}
entry, err := handle.Entry(0)
if err != nil {
return nil, err
}
if entry.KeyStatus() != keyset.Enabled {
return nil, fmt.Errorf("unsupported key status: %v, want %v", entry.KeyStatus(), keyset.Enabled)
}
publicKey, ok := entry.Key().(*rsassapkcs1.PublicKey)
if !ok {
return nil, fmt.Errorf("invalid key type: %T, want *rsassapkcs1.PublicKey", entry.Key())
}
params := publicKey.Parameters().(*rsassapkcs1.Parameters)
if params.HashType() != rsassapkcs1.SHA256 {
return nil, fmt.Errorf("unsupported hash type: %v, want %v", params.HashType(), rsassapkcs1.SHA256)
}
if params.PublicExponent() != f4 {
return nil, fmt.Errorf("invalid public exponent: %v, want %v", params.PublicExponent(), f4)
}
if params.Variant() != rsassapkcs1.VariantNoPrefix {
return nil, fmt.Errorf("unsupported output prefix variant: %v, want %v", params.Variant(), rsassapkcs1.VariantNoPrefix)
}
encoded, err := x509.MarshalPKIXPublicKey(
&rsa.PublicKey{
N: new(big.Int).SetBytes(publicKey.Modulus()),
E: params.PublicExponent(),
})
if err != nil {
return nil, fmt.Errorf("x509.MarshalPKIXPublicKey failed: %v", err)
}
block := &pem.Block{
Type: "PUBLIC KEY",
Bytes: encoded,
}
return pem.EncodeToMemory(block), nil
}