Procházet zdrojové kódy

Add explicit unmarshaller for signing and key agreement public keys (#1777)

Nate Brown před 3 měsíci
rodič
revize
6aa3363d85
2 změnil soubory, kde provedl 95 přidání a 45 odebrání
  1. 32 2
      cert/pem.go
  2. 63 43
      cert/pem_test.go

+ 32 - 2
cert/pem.go

@@ -148,6 +148,9 @@ func MarshalSigningPublicKeyToPEM(curve Curve, b []byte) []byte {
 	}
 	}
 }
 }
 
 
+// UnmarshalPublicKeyFromPEM will try to unmarshal the first pem block in a byte array, returning any non
+// consumed data or an error on failure. Only key-agreement (ECDH) public key banners are accepted.
+// Use UnmarshalSigningPublicKeyFromPEM for Ed25519/ECDSA banners.
 func UnmarshalPublicKeyFromPEM(b []byte) ([]byte, []byte, Curve, error) {
 func UnmarshalPublicKeyFromPEM(b []byte) ([]byte, []byte, Curve, error) {
 	k, r := pem.Decode(b)
 	k, r := pem.Decode(b)
 	if k == nil {
 	if k == nil {
@@ -156,10 +159,10 @@ func UnmarshalPublicKeyFromPEM(b []byte) ([]byte, []byte, Curve, error) {
 	var expectedLen int
 	var expectedLen int
 	var curve Curve
 	var curve Curve
 	switch k.Type {
 	switch k.Type {
-	case X25519PublicKeyBanner, Ed25519PublicKeyBanner:
+	case X25519PublicKeyBanner:
 		expectedLen = 32
 		expectedLen = 32
 		curve = Curve_CURVE25519
 		curve = Curve_CURVE25519
-	case P256PublicKeyBanner, ECDSAP256PublicKeyBanner:
+	case P256PublicKeyBanner:
 		// Uncompressed
 		// Uncompressed
 		expectedLen = 65
 		expectedLen = 65
 		curve = Curve_P256
 		curve = Curve_P256
@@ -172,6 +175,33 @@ func UnmarshalPublicKeyFromPEM(b []byte) ([]byte, []byte, Curve, error) {
 	return k.Bytes, r, curve, nil
 	return k.Bytes, r, curve, nil
 }
 }
 
 
+// UnmarshalSigningPublicKeyFromPEM will try to unmarshal the first pem block in a byte array, returning any non
+// consumed data or an error on failure. Only Ed25519/ECDSA public key banners are accepted.
+// Use UnmarshalPublicKeyFromPEM for X25519/P256 (ECDH) banners.
+func UnmarshalSigningPublicKeyFromPEM(b []byte) ([]byte, []byte, Curve, error) {
+	k, r := pem.Decode(b)
+	if k == nil {
+		return nil, r, 0, fmt.Errorf("input did not contain a valid PEM encoded block")
+	}
+	var expectedLen int
+	var curve Curve
+	switch k.Type {
+	case Ed25519PublicKeyBanner:
+		expectedLen = 32
+		curve = Curve_CURVE25519
+	case ECDSAP256PublicKeyBanner:
+		// Uncompressed
+		expectedLen = 65
+		curve = Curve_P256
+	default:
+		return nil, r, 0, fmt.Errorf("bytes did not contain a proper Ed25519/ECDSA public key banner")
+	}
+	if len(k.Bytes) != expectedLen {
+		return nil, r, 0, fmt.Errorf("key was not %d bytes, is invalid %s public key", expectedLen, curve)
+	}
+	return k.Bytes, r, curve, nil
+}
+
 func MarshalPrivateKeyToPEM(curve Curve, b []byte) []byte {
 func MarshalPrivateKeyToPEM(curve Curve, b []byte) []byte {
 	switch curve {
 	switch curve {
 	case Curve_CURVE25519:
 	case Curve_CURVE25519:

+ 63 - 43
cert/pem_test.go

@@ -255,14 +255,26 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
 func TestUnmarshalPublicKeyFromPEM(t *testing.T) {
 func TestUnmarshalPublicKeyFromPEM(t *testing.T) {
 	t.Parallel()
 	t.Parallel()
 	pubKey := []byte(`# A good key
 	pubKey := []byte(`# A good key
------BEGIN NEBULA ED25519 PUBLIC KEY-----
+-----BEGIN NEBULA X25519 PUBLIC KEY-----
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
------END NEBULA ED25519 PUBLIC KEY-----
+-----END NEBULA X25519 PUBLIC KEY-----
+`)
+	pubP256Key := []byte(`# A good key
+-----BEGIN NEBULA P256 PUBLIC KEY-----
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAA=
+-----END NEBULA P256 PUBLIC KEY-----
+`)
+	signingKey := []byte(`# A signing key has the wrong scope for this function
+-----BEGIN NEBULA ECDSA P256 PUBLIC KEY-----
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAA=
+-----END NEBULA ECDSA P256 PUBLIC KEY-----
 `)
 `)
 	shortKey := []byte(`# A short key
 	shortKey := []byte(`# A short key
------BEGIN NEBULA ED25519 PUBLIC KEY-----
+-----BEGIN NEBULA X25519 PUBLIC KEY-----
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
------END NEBULA ED25519 PUBLIC KEY-----
+-----END NEBULA X25519 PUBLIC KEY-----
 `)
 `)
 	invalidBanner := []byte(`# Invalid banner
 	invalidBanner := []byte(`# Invalid banner
 -----BEGIN NOT A NEBULA PUBLIC KEY-----
 -----BEGIN NOT A NEBULA PUBLIC KEY-----
@@ -270,65 +282,74 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
 -----END NOT A NEBULA PUBLIC KEY-----
 -----END NOT A NEBULA PUBLIC KEY-----
 `)
 `)
 	invalidPem := []byte(`# Not a valid PEM format
 	invalidPem := []byte(`# Not a valid PEM format
--BEGIN NEBULA ED25519 PUBLIC KEY-----
+-BEGIN NEBULA X25519 PUBLIC KEY-----
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
--END NEBULA ED25519 PUBLIC KEY-----`)
+-END NEBULA X25519 PUBLIC KEY-----`)
 
 
-	keyBundle := appendByteSlices(pubKey, shortKey, invalidBanner, invalidPem)
+	keyBundle := appendByteSlices(pubKey, pubP256Key, signingKey, shortKey, invalidBanner, invalidPem)
 
 
-	// Success test case
+	// X25519 key
 	k, rest, curve, err := UnmarshalPublicKeyFromPEM(keyBundle)
 	k, rest, curve, err := UnmarshalPublicKeyFromPEM(keyBundle)
 	assert.Len(t, k, 32)
 	assert.Len(t, k, 32)
+	require.NoError(t, err)
+	assert.Equal(t, rest, appendByteSlices(pubP256Key, signingKey, shortKey, invalidBanner, invalidPem))
 	assert.Equal(t, Curve_CURVE25519, curve)
 	assert.Equal(t, Curve_CURVE25519, curve)
+
+	// P256 key
+	k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
+	assert.Len(t, k, 65)
 	require.NoError(t, err)
 	require.NoError(t, err)
+	assert.Equal(t, rest, appendByteSlices(signingKey, shortKey, invalidBanner, invalidPem))
+	assert.Equal(t, Curve_P256, curve)
+
+	// Reject a signing public key (Ed25519/ECDSA banner)
+	k, rest, _, err = UnmarshalPublicKeyFromPEM(rest)
+	assert.Nil(t, k)
 	assert.Equal(t, rest, appendByteSlices(shortKey, invalidBanner, invalidPem))
 	assert.Equal(t, rest, appendByteSlices(shortKey, invalidBanner, invalidPem))
+	require.EqualError(t, err, "bytes did not contain a proper public key banner")
 
 
 	// Fail due to short key
 	// Fail due to short key
-	k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
+	k, rest, _, err = UnmarshalPublicKeyFromPEM(rest)
 	assert.Nil(t, k)
 	assert.Nil(t, k)
-	assert.Equal(t, Curve_CURVE25519, curve)
 	assert.Equal(t, rest, appendByteSlices(invalidBanner, invalidPem))
 	assert.Equal(t, rest, appendByteSlices(invalidBanner, invalidPem))
 	require.EqualError(t, err, "key was not 32 bytes, is invalid CURVE25519 public key")
 	require.EqualError(t, err, "key was not 32 bytes, is invalid CURVE25519 public key")
 
 
 	// Fail due to invalid banner
 	// Fail due to invalid banner
-	k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
+	k, rest, _, err = UnmarshalPublicKeyFromPEM(rest)
 	assert.Nil(t, k)
 	assert.Nil(t, k)
-	assert.Equal(t, Curve_CURVE25519, curve)
 	require.EqualError(t, err, "bytes did not contain a proper public key banner")
 	require.EqualError(t, err, "bytes did not contain a proper public key banner")
 	assert.Equal(t, rest, invalidPem)
 	assert.Equal(t, rest, invalidPem)
 
 
 	// Fail due to invalid PEM format, because
 	// Fail due to invalid PEM format, because
 	// it's missing the requisite pre-encapsulation boundary.
 	// it's missing the requisite pre-encapsulation boundary.
-	k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
+	k, rest, _, err = UnmarshalPublicKeyFromPEM(rest)
 	assert.Nil(t, k)
 	assert.Nil(t, k)
-	assert.Equal(t, Curve_CURVE25519, curve)
 	assert.Equal(t, rest, invalidPem)
 	assert.Equal(t, rest, invalidPem)
 	require.EqualError(t, err, "input did not contain a valid PEM encoded block")
 	require.EqualError(t, err, "input did not contain a valid PEM encoded block")
 }
 }
 
 
-func TestUnmarshalX25519PublicKey(t *testing.T) {
+func TestUnmarshalSigningPublicKeyFromPEM(t *testing.T) {
 	t.Parallel()
 	t.Parallel()
 	pubKey := []byte(`# A good key
 	pubKey := []byte(`# A good key
------BEGIN NEBULA X25519 PUBLIC KEY-----
+-----BEGIN NEBULA ED25519 PUBLIC KEY-----
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
------END NEBULA X25519 PUBLIC KEY-----
+-----END NEBULA ED25519 PUBLIC KEY-----
 `)
 `)
 	pubP256Key := []byte(`# A good key
 	pubP256Key := []byte(`# A good key
------BEGIN NEBULA P256 PUBLIC KEY-----
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAA=
------END NEBULA P256 PUBLIC KEY-----
-`)
-	oldPubP256Key := []byte(`# A good key
 -----BEGIN NEBULA ECDSA P256 PUBLIC KEY-----
 -----BEGIN NEBULA ECDSA P256 PUBLIC KEY-----
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 AAAAAAAAAAAAAAAAAAAAAAA=
 AAAAAAAAAAAAAAAAAAAAAAA=
 -----END NEBULA ECDSA P256 PUBLIC KEY-----
 -----END NEBULA ECDSA P256 PUBLIC KEY-----
 `)
 `)
-	shortKey := []byte(`# A short key
+	ecdhKey := []byte(`# A key-agreement key has the wrong scope for this function
 -----BEGIN NEBULA X25519 PUBLIC KEY-----
 -----BEGIN NEBULA X25519 PUBLIC KEY-----
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
 -----END NEBULA X25519 PUBLIC KEY-----
 -----END NEBULA X25519 PUBLIC KEY-----
+`)
+	shortKey := []byte(`# A short key
+-----BEGIN NEBULA ED25519 PUBLIC KEY-----
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
+-----END NEBULA ED25519 PUBLIC KEY-----
 `)
 `)
 	invalidBanner := []byte(`# Invalid banner
 	invalidBanner := []byte(`# Invalid banner
 -----BEGIN NOT A NEBULA PUBLIC KEY-----
 -----BEGIN NOT A NEBULA PUBLIC KEY-----
@@ -336,48 +357,47 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
 -----END NOT A NEBULA PUBLIC KEY-----
 -----END NOT A NEBULA PUBLIC KEY-----
 `)
 `)
 	invalidPem := []byte(`# Not a valid PEM format
 	invalidPem := []byte(`# Not a valid PEM format
--BEGIN NEBULA X25519 PUBLIC KEY-----
+-BEGIN NEBULA ED25519 PUBLIC KEY-----
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
--END NEBULA X25519 PUBLIC KEY-----`)
+-END NEBULA ED25519 PUBLIC KEY-----`)
 
 
-	keyBundle := appendByteSlices(pubKey, pubP256Key, oldPubP256Key, shortKey, invalidBanner, invalidPem)
+	keyBundle := appendByteSlices(pubKey, pubP256Key, ecdhKey, shortKey, invalidBanner, invalidPem)
 
 
-	// Success test case
-	k, rest, curve, err := UnmarshalPublicKeyFromPEM(keyBundle)
+	// Ed25519 key
+	k, rest, curve, err := UnmarshalSigningPublicKeyFromPEM(keyBundle)
 	assert.Len(t, k, 32)
 	assert.Len(t, k, 32)
 	require.NoError(t, err)
 	require.NoError(t, err)
-	assert.Equal(t, rest, appendByteSlices(pubP256Key, oldPubP256Key, shortKey, invalidBanner, invalidPem))
+	assert.Equal(t, rest, appendByteSlices(pubP256Key, ecdhKey, shortKey, invalidBanner, invalidPem))
 	assert.Equal(t, Curve_CURVE25519, curve)
 	assert.Equal(t, Curve_CURVE25519, curve)
 
 
-	// Success test case
-	k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
+	// ECDSA P256 key
+	k, rest, curve, err = UnmarshalSigningPublicKeyFromPEM(rest)
 	assert.Len(t, k, 65)
 	assert.Len(t, k, 65)
 	require.NoError(t, err)
 	require.NoError(t, err)
-	assert.Equal(t, rest, appendByteSlices(oldPubP256Key, shortKey, invalidBanner, invalidPem))
+	assert.Equal(t, rest, appendByteSlices(ecdhKey, shortKey, invalidBanner, invalidPem))
 	assert.Equal(t, Curve_P256, curve)
 	assert.Equal(t, Curve_P256, curve)
 
 
-	// Success test case
-	k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
-	assert.Len(t, k, 65)
-	require.NoError(t, err)
+	// Reject a key-agreement public key (X25519/P256 banner)
+	k, rest, _, err = UnmarshalSigningPublicKeyFromPEM(rest)
+	assert.Nil(t, k)
 	assert.Equal(t, rest, appendByteSlices(shortKey, invalidBanner, invalidPem))
 	assert.Equal(t, rest, appendByteSlices(shortKey, invalidBanner, invalidPem))
-	assert.Equal(t, Curve_P256, curve)
+	require.EqualError(t, err, "bytes did not contain a proper Ed25519/ECDSA public key banner")
 
 
 	// Fail due to short key
 	// Fail due to short key
-	k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
+	k, rest, _, err = UnmarshalSigningPublicKeyFromPEM(rest)
 	assert.Nil(t, k)
 	assert.Nil(t, k)
 	assert.Equal(t, rest, appendByteSlices(invalidBanner, invalidPem))
 	assert.Equal(t, rest, appendByteSlices(invalidBanner, invalidPem))
 	require.EqualError(t, err, "key was not 32 bytes, is invalid CURVE25519 public key")
 	require.EqualError(t, err, "key was not 32 bytes, is invalid CURVE25519 public key")
 
 
 	// Fail due to invalid banner
 	// Fail due to invalid banner
-	k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
+	k, rest, _, err = UnmarshalSigningPublicKeyFromPEM(rest)
 	assert.Nil(t, k)
 	assert.Nil(t, k)
-	require.EqualError(t, err, "bytes did not contain a proper public key banner")
+	require.EqualError(t, err, "bytes did not contain a proper Ed25519/ECDSA public key banner")
 	assert.Equal(t, rest, invalidPem)
 	assert.Equal(t, rest, invalidPem)
 
 
 	// Fail due to invalid PEM format, because
 	// Fail due to invalid PEM format, because
 	// it's missing the requisite pre-encapsulation boundary.
 	// it's missing the requisite pre-encapsulation boundary.
-	k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
+	k, rest, _, err = UnmarshalSigningPublicKeyFromPEM(rest)
 	assert.Nil(t, k)
 	assert.Nil(t, k)
 	assert.Equal(t, rest, invalidPem)
 	assert.Equal(t, rest, invalidPem)
 	require.EqualError(t, err, "input did not contain a valid PEM encoded block")
 	require.EqualError(t, err, "input did not contain a valid PEM encoded block")