Skip to content

Commit 7aac97b

Browse files
authored
Merge pull request #54 from TheManticoreProject/bcrypt-ecc-key
[enhancement] Implemented BCRYPT_ECC_KEY, fixes #47
2 parents dc37d72 + 6ba481e commit 7aac97b

File tree

6 files changed

+592
-0
lines changed

6 files changed

+592
-0
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package keys
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/TheManticoreProject/Manticore/windows/cng/bcrypt/keys/blob"
9+
"github.com/TheManticoreProject/Manticore/windows/cng/bcrypt/keys/headers"
10+
"github.com/TheManticoreProject/Manticore/windows/cng/bcrypt/keys/magic"
11+
)
12+
13+
type BCRYPT_ECC_PRIVATE_KEY struct {
14+
// Magic is the magic signature of the key.
15+
Magic magic.BCRYPT_KEY_BLOB
16+
17+
// Header is the header of the key.
18+
Header headers.BCRYPT_ECC_KEY_BLOB
19+
20+
// Content is the content of the key.
21+
Content blob.BCRYPT_ECC_PRIVATE_BLOB
22+
}
23+
24+
// Unmarshal parses the provided byte slice into the BCRYPT_ECC_PRIVATE_KEY structure.
25+
//
26+
// Parameters:
27+
// - value: A byte slice containing the raw ECC private key to be parsed.
28+
//
29+
// Returns:
30+
// - The number of bytes read from the byte slice.
31+
// - An error if the parsing fails, otherwise nil.
32+
//
33+
// Note:
34+
// The function expects the byte slice to follow the ECC private key format, starting with the BCRYPT_ECC_KEY_BLOB header.
35+
// It extracts the X, Y and D big-endian values from the byte slice and stores them in the BCRYPT_ECC_PRIVATE_KEY structure.
36+
func (k *BCRYPT_ECC_PRIVATE_KEY) Unmarshal(value []byte) (int, error) {
37+
// Need at least 8 bytes for magic (4) + header (4)
38+
if len(value) < 8 {
39+
return 0, errors.New("buffer too small for BCRYPT_ECC_PRIVATE_KEY, header too short (at least 8 bytes are required)")
40+
}
41+
42+
bytesRead := 0
43+
44+
// Unmarshalling magic
45+
bytesReadMagic, err := k.Magic.Unmarshal(value[:4])
46+
if err != nil {
47+
return 0, err
48+
}
49+
if !isValidECCPrivateMagic(k.Magic.Magic) {
50+
return 0, fmt.Errorf("invalid ECC private key magic: 0x%08x", k.Magic.Magic)
51+
}
52+
bytesRead += bytesReadMagic
53+
54+
// Unmarshalling header
55+
bytesReadHeader, err := k.Header.Unmarshal(value[bytesRead:])
56+
if err != nil {
57+
return 0, err
58+
}
59+
bytesRead += bytesReadHeader
60+
61+
// Unmarshalling content
62+
bytesReadContent, err := k.Content.Unmarshal(k.Header, value[bytesRead:])
63+
if err != nil {
64+
return 0, err
65+
}
66+
bytesRead += bytesReadContent
67+
68+
return bytesRead, nil
69+
}
70+
71+
// Marshal returns the raw bytes of the BCRYPT_ECC_PRIVATE_KEY structure.
72+
//
73+
// Returns:
74+
// - A byte slice representing the raw bytes of the BCRYPT_ECC_PRIVATE_KEY structure.
75+
func (k *BCRYPT_ECC_PRIVATE_KEY) Marshal() ([]byte, error) {
76+
if !isValidECCPrivateMagic(k.Magic.Magic) {
77+
return nil, fmt.Errorf("invalid ECC private key magic for marshal: 0x%08x", k.Magic.Magic)
78+
}
79+
80+
marshalledData := []byte{}
81+
82+
// Marshalling magic (kept as-is; caller must select appropriate curve/type magic)
83+
marshalledMagic, err := k.Magic.Marshal()
84+
if err != nil {
85+
return nil, err
86+
}
87+
marshalledData = append(marshalledData, marshalledMagic...)
88+
89+
// Marshalling header
90+
marshalledHeader, err := k.Header.Marshal()
91+
if err != nil {
92+
return nil, err
93+
}
94+
marshalledData = append(marshalledData, marshalledHeader...)
95+
96+
// Marshalling content
97+
marshalledContent, err := k.Content.Marshal()
98+
if err != nil {
99+
return nil, err
100+
}
101+
marshalledData = append(marshalledData, marshalledContent...)
102+
103+
return marshalledData, nil
104+
}
105+
106+
// Describe prints a detailed description of the BCRYPT_ECC_PRIVATE_KEY structure.
107+
//
108+
// Parameters:
109+
// - indent: An integer representing the indentation level for the printed output.
110+
//
111+
// Note:
112+
// The function prints the Header and Data of the BCRYPT_ECC_PRIVATE_KEY structure.
113+
// The output is formatted with the specified indentation level to improve readability.
114+
func (k *BCRYPT_ECC_PRIVATE_KEY) Describe(indent int) {
115+
indentPrompt := strings.Repeat(" │ ", indent)
116+
fmt.Printf("%s<\x1b[93mBCRYPT_ECC_PRIVATE_KEY\x1b[0m>\n", indentPrompt)
117+
k.Magic.Describe(indent + 1)
118+
k.Header.Describe(indent + 1)
119+
k.Content.Describe(indent + 1)
120+
fmt.Printf("%s└───\n", indentPrompt)
121+
}
122+
123+
func isValidECCPrivateMagic(m uint32) bool {
124+
switch m {
125+
case magic.BCRYPT_ECDH_PRIVATE_P256_MAGIC,
126+
magic.BCRYPT_ECDH_PRIVATE_P384_MAGIC,
127+
magic.BCRYPT_ECDH_PRIVATE_P521_MAGIC,
128+
magic.BCRYPT_ECDSA_PRIVATE_P256_MAGIC,
129+
magic.BCRYPT_ECDSA_PRIVATE_P384_MAGIC,
130+
magic.BCRYPT_ECDSA_PRIVATE_P521_MAGIC:
131+
return true
132+
default:
133+
return false
134+
}
135+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package keys
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/TheManticoreProject/Manticore/windows/cng/bcrypt/keys/blob"
9+
"github.com/TheManticoreProject/Manticore/windows/cng/bcrypt/keys/headers"
10+
"github.com/TheManticoreProject/Manticore/windows/cng/bcrypt/keys/magic"
11+
)
12+
13+
type BCRYPT_ECC_PUBLIC_KEY struct {
14+
// Magic is the magic signature of the key.
15+
Magic magic.BCRYPT_KEY_BLOB
16+
17+
// Header is the header of the key.
18+
Header headers.BCRYPT_ECC_KEY_BLOB
19+
20+
// Content is the content of the key.
21+
Content blob.BCRYPT_ECC_PUBLIC_BLOB
22+
}
23+
24+
// Unmarshal parses the provided byte slice into the BCRYPT_ECC_PUBLIC_KEY structure.
25+
//
26+
// Parameters:
27+
// - value: A byte slice containing the raw ECC public key to be parsed.
28+
//
29+
// Returns:
30+
// - The number of bytes read from the byte slice.
31+
// - An error if the parsing fails, otherwise nil.
32+
//
33+
// Note:
34+
// The function expects the byte slice to follow the ECC public key format, starting with the BCRYPT_ECC_KEY_BLOB header.
35+
// It extracts the X and Y big-endian values from the byte slice and stores them in the BCRYPT_ECC_PUBLIC_KEY structure.
36+
func (k *BCRYPT_ECC_PUBLIC_KEY) Unmarshal(value []byte) (int, error) {
37+
// Need at least 8 bytes for magic (4) + header (4)
38+
if len(value) < 8 {
39+
return 0, errors.New("buffer too small for BCRYPT_ECC_PUBLIC_KEY, header too short (at least 8 bytes are required)")
40+
}
41+
42+
bytesRead := 0
43+
44+
// Unmarshalling magic
45+
bytesReadMagic, err := k.Magic.Unmarshal(value[:4])
46+
if err != nil {
47+
return 0, err
48+
}
49+
if !isValidECCPublicMagic(k.Magic.Magic) {
50+
return 0, fmt.Errorf("invalid ECC public key magic: 0x%08x", k.Magic.Magic)
51+
}
52+
bytesRead += bytesReadMagic
53+
54+
// Unmarshalling header
55+
bytesReadHeader, err := k.Header.Unmarshal(value[bytesRead:])
56+
if err != nil {
57+
return 0, err
58+
}
59+
bytesRead += bytesReadHeader
60+
61+
// Unmarshalling content
62+
bytesReadContent, err := k.Content.Unmarshal(k.Header, value[bytesRead:])
63+
if err != nil {
64+
return 0, err
65+
}
66+
bytesRead += bytesReadContent
67+
68+
return bytesRead, nil
69+
}
70+
71+
// Marshal returns the raw bytes of the BCRYPT_ECC_PUBLIC_KEY structure.
72+
//
73+
// Returns:
74+
// - A byte slice representing the raw bytes of the BCRYPT_ECC_PUBLIC_KEY structure.
75+
func (k *BCRYPT_ECC_PUBLIC_KEY) Marshal() ([]byte, error) {
76+
if !isValidECCPublicMagic(k.Magic.Magic) {
77+
return nil, fmt.Errorf("invalid ECC public key magic for marshal: 0x%08x", k.Magic.Magic)
78+
}
79+
80+
marshalledData := []byte{}
81+
82+
// Marshalling magic (kept as-is; caller must select appropriate curve/type magic)
83+
marshalledMagic, err := k.Magic.Marshal()
84+
if err != nil {
85+
return nil, err
86+
}
87+
marshalledData = append(marshalledData, marshalledMagic...)
88+
89+
// Marshalling header
90+
marshalledHeader, err := k.Header.Marshal()
91+
if err != nil {
92+
return nil, err
93+
}
94+
marshalledData = append(marshalledData, marshalledHeader...)
95+
96+
// Marshalling content
97+
marshalledContent, err := k.Content.Marshal()
98+
if err != nil {
99+
return nil, err
100+
}
101+
marshalledData = append(marshalledData, marshalledContent...)
102+
103+
return marshalledData, nil
104+
}
105+
106+
// Describe prints a detailed description of the BCRYPT_ECC_PUBLIC_KEY structure.
107+
//
108+
// Parameters:
109+
// - indent: An integer representing the indentation level for the printed output.
110+
//
111+
// Note:
112+
// The function prints the Header and Data of the BCRYPT_ECC_PUBLIC_KEY structure.
113+
// The output is formatted with the specified indentation level to improve readability.
114+
func (k *BCRYPT_ECC_PUBLIC_KEY) Describe(indent int) {
115+
indentPrompt := strings.Repeat(" │ ", indent)
116+
fmt.Printf("%s<\x1b[93mBCRYPT_ECC_PUBLIC_KEY\x1b[0m>\n", indentPrompt)
117+
k.Magic.Describe(indent + 1)
118+
k.Header.Describe(indent + 1)
119+
k.Content.Describe(indent + 1)
120+
fmt.Printf("%s└───\n", indentPrompt)
121+
}
122+
123+
func isValidECCPublicMagic(m uint32) bool {
124+
switch m {
125+
case magic.BCRYPT_ECDH_PUBLIC_P256_MAGIC,
126+
magic.BCRYPT_ECDH_PUBLIC_P384_MAGIC,
127+
magic.BCRYPT_ECDH_PUBLIC_P521_MAGIC,
128+
magic.BCRYPT_ECDSA_PUBLIC_P256_MAGIC,
129+
magic.BCRYPT_ECDSA_PUBLIC_P384_MAGIC,
130+
magic.BCRYPT_ECDSA_PUBLIC_P521_MAGIC:
131+
return true
132+
default:
133+
return false
134+
}
135+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package blob
2+
3+
import (
4+
"bytes"
5+
"encoding/hex"
6+
"fmt"
7+
"strings"
8+
9+
"github.com/TheManticoreProject/Manticore/windows/cng/bcrypt/keys/headers"
10+
)
11+
12+
// BCRYPT_ECC_PRIVATE_BLOB represents the content of an ECC private key BLOB in memory.
13+
//
14+
// Layout in memory (following the BCRYPT_ECC_KEY_BLOB header):
15+
//
16+
// BCRYPT_ECC_KEY_BLOB
17+
// BYTE X[cbKey] // Big-endian.
18+
// BYTE Y[cbKey] // Big-endian.
19+
// BYTE d[cbKey] // Big-endian.
20+
//
21+
// See:
22+
// https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_ecckey_blob
23+
type BCRYPT_ECC_PRIVATE_BLOB struct {
24+
// X is the X coordinate of the public point. Big-endian.
25+
X []byte
26+
27+
// Y is the Y coordinate of the public point. Big-endian.
28+
Y []byte
29+
30+
// D is the private scalar. Big-endian.
31+
D []byte
32+
}
33+
34+
// Unmarshal parses the provided byte slice into the BCRYPT_ECC_PRIVATE_BLOB structure.
35+
//
36+
// Parameters:
37+
// - keyHeader: The already-parsed BCRYPT_ECC_KEY_BLOB header, providing cbKey.
38+
// - value: A byte slice containing the raw ECC private key BLOB content to be parsed,
39+
// starting immediately after the header.
40+
//
41+
// Returns:
42+
// - The number of bytes read from the byte slice.
43+
// - An error if the parsing fails, otherwise nil.
44+
func (b *BCRYPT_ECC_PRIVATE_BLOB) Unmarshal(keyHeader headers.BCRYPT_ECC_KEY_BLOB, value []byte) (int, error) {
45+
bytesRead := 0
46+
cbKey := int(keyHeader.KeySize)
47+
48+
if cbKey > len(value)-bytesRead {
49+
return 0, fmt.Errorf("buffer too small for BCRYPT_ECC_PRIVATE_BLOB, not enough bytes for unmarshalling X")
50+
}
51+
b.X = value[bytesRead : bytesRead+cbKey]
52+
bytesRead += cbKey
53+
54+
if cbKey > len(value)-bytesRead {
55+
return 0, fmt.Errorf("buffer too small for BCRYPT_ECC_PRIVATE_BLOB, not enough bytes for unmarshalling Y")
56+
}
57+
b.Y = value[bytesRead : bytesRead+cbKey]
58+
bytesRead += cbKey
59+
60+
if cbKey > len(value)-bytesRead {
61+
return 0, fmt.Errorf("buffer too small for BCRYPT_ECC_PRIVATE_BLOB, not enough bytes for unmarshalling D")
62+
}
63+
b.D = value[bytesRead : bytesRead+cbKey]
64+
bytesRead += cbKey
65+
66+
return bytesRead, nil
67+
}
68+
69+
// Marshal returns the raw bytes of the BCRYPT_ECC_PRIVATE_BLOB structure.
70+
//
71+
// Returns:
72+
// - A byte slice representing the raw bytes of the BCRYPT_ECC_PRIVATE_BLOB structure.
73+
func (b *BCRYPT_ECC_PRIVATE_BLOB) Marshal() ([]byte, error) {
74+
marshalledData := []byte{}
75+
76+
marshalledData = append(marshalledData, b.X...)
77+
marshalledData = append(marshalledData, b.Y...)
78+
marshalledData = append(marshalledData, b.D...)
79+
80+
return marshalledData, nil
81+
}
82+
83+
// Describe prints the BCRYPT_ECC_PRIVATE_BLOB structure to the console.
84+
//
85+
// Parameters:
86+
// - indent: The number of spaces to indent the output.
87+
func (b *BCRYPT_ECC_PRIVATE_BLOB) Describe(indent int) {
88+
indentPrompt := strings.Repeat(" │ ", indent)
89+
fmt.Printf("%s<\x1b[93mBCRYPT_ECC_PRIVATE_BLOB (content)\x1b[0m>\n", indentPrompt)
90+
fmt.Printf("%s │ \x1b[93mX\x1b[0m: 0x%s\n", indentPrompt, hex.EncodeToString(b.X))
91+
fmt.Printf("%s │ \x1b[93mY\x1b[0m: 0x%s\n", indentPrompt, hex.EncodeToString(b.Y))
92+
fmt.Printf("%s │ \x1b[93mD\x1b[0m: 0x%s\n", indentPrompt, hex.EncodeToString(b.D))
93+
fmt.Printf("%s └───\n", indentPrompt)
94+
}
95+
96+
// Equal checks if two BCRYPT_ECC_PRIVATE_BLOB structures are equal.
97+
//
98+
// Parameters:
99+
// - other: The BCRYPT_ECC_PRIVATE_BLOB structure to compare to.
100+
//
101+
// Returns:
102+
// - True if the two BCRYPT_ECC_PRIVATE_BLOB structures are equal, false otherwise.
103+
func (b *BCRYPT_ECC_PRIVATE_BLOB) Equal(other *BCRYPT_ECC_PRIVATE_BLOB) bool {
104+
return bytes.Equal(b.X, other.X) && bytes.Equal(b.Y, other.Y) && bytes.Equal(b.D, other.D)
105+
}

0 commit comments

Comments
 (0)