Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitcd3a104

Browse files
committed
fix(agentssh): ensure RSA key generation always produces valid keys
Change-Id: I0a453e1e1f8c638e40e7a4b87a6d0d7299e1cb5dSigned-off-by: Thomas Kosiewski <tk@coder.com>
1 parent546d915 commitcd3a104

File tree

3 files changed

+139
-72
lines changed

3 files changed

+139
-72
lines changed

‎agent/agentrsa/key.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package agentrsa
2+
3+
import (
4+
"crypto/rsa"
5+
"math/big"
6+
"math/rand"
7+
)
8+
9+
// GenerateDeterministicKey generates an RSA private key deterministically based on the provided seed.
10+
// This function uses a deterministic random source to generate the primes p and q, ensuring that the
11+
// same seed will always produce the same private key. The generated key is 2048 bits in size.
12+
//
13+
// Reference: https://pkg.go.dev/crypto/rsa#GenerateKey
14+
funcGenerateDeterministicKey(seedint64)*rsa.PrivateKey {
15+
// Since the standard lib purposefully does not generate
16+
// deterministic rsa keys, we need to do it ourselves.
17+
18+
// Create deterministic random source
19+
// nolint: gosec
20+
deterministicRand:=rand.New(rand.NewSource(seed))
21+
22+
// Use fixed values for p and q based on the seed
23+
p:=big.NewInt(0)
24+
q:=big.NewInt(0)
25+
e:=big.NewInt(65537)// Standard RSA public exponent
26+
27+
for {
28+
// Generate deterministic primes using the seeded random
29+
// Each prime should be ~1024 bits to get a 2048-bit key
30+
for {
31+
p.SetBit(p,1024,1)// Ensure it's large enough
32+
fori:=range1024 {
33+
ifdeterministicRand.Int63()%2==1 {
34+
p.SetBit(p,i,1)
35+
}else {
36+
p.SetBit(p,i,0)
37+
}
38+
}
39+
p1:=new(big.Int).Sub(p,big.NewInt(1))
40+
ifp.ProbablyPrime(20)&&new(big.Int).GCD(nil,nil,e,p1).Cmp(big.NewInt(1))==0 {
41+
break
42+
}
43+
}
44+
45+
for {
46+
q.SetBit(q,1024,1)// Ensure it's large enough
47+
fori:=range1024 {
48+
ifdeterministicRand.Int63()%2==1 {
49+
q.SetBit(q,i,1)
50+
}else {
51+
q.SetBit(q,i,0)
52+
}
53+
}
54+
q1:=new(big.Int).Sub(q,big.NewInt(1))
55+
ifq.ProbablyPrime(20)&&p.Cmp(q)!=0&&new(big.Int).GCD(nil,nil,e,q1).Cmp(big.NewInt(1))==0 {
56+
break
57+
}
58+
}
59+
60+
// Calculate phi = (p-1) * (q-1)
61+
p1:=new(big.Int).Sub(p,big.NewInt(1))
62+
q1:=new(big.Int).Sub(q,big.NewInt(1))
63+
phi:=new(big.Int).Mul(p1,q1)
64+
65+
// Calculate private exponent d
66+
d:=new(big.Int).ModInverse(e,phi)
67+
ifd!=nil {
68+
// Calculate n = p * q
69+
n:=new(big.Int).Mul(p,q)
70+
71+
// Create the private key
72+
privateKey:=&rsa.PrivateKey{
73+
PublicKey: rsa.PublicKey{
74+
N:n,
75+
E:int(e.Int64()),
76+
},
77+
D:d,
78+
Primes: []*big.Int{p,q},
79+
}
80+
81+
// Compute precomputed values
82+
privateKey.Precompute()
83+
84+
returnprivateKey
85+
}
86+
}
87+
}

‎agent/agentrsa/key_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package agentrsa_test
2+
3+
import (
4+
"crypto/rsa"
5+
"math/rand/v2"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
10+
"github.com/coder/coder/v2/agent/agentrsa"
11+
)
12+
13+
funcTestGenerateDeterministicKey(t*testing.T) {
14+
t.Parallel()
15+
16+
key1:=agentrsa.GenerateDeterministicKey(1234)
17+
key2:=agentrsa.GenerateDeterministicKey(1234)
18+
19+
assert.Equal(t,key1,key2)
20+
assert.EqualExportedValues(t,key1,key2)
21+
}
22+
23+
varresult*rsa.PrivateKey
24+
25+
funcBenchmarkGenerateDeterministicKey(b*testing.B) {
26+
varr*rsa.PrivateKey
27+
28+
forrangeb.N {
29+
// always record the result of DeterministicPrivateKey to prevent
30+
// the compiler eliminating the function call.
31+
r=agentrsa.GenerateDeterministicKey(rand.Int64())
32+
}
33+
34+
// always store the result to a package level variable
35+
// so the compiler cannot eliminate the Benchmark itself.
36+
result=r
37+
}
38+
39+
funcFuzzGenerateDeterministicKey(f*testing.F) {
40+
testcases:= []int64{0,1234,1010101010}
41+
for_,tc:=rangetestcases {
42+
f.Add(tc)// Use f.Add to provide a seed corpus
43+
}
44+
f.Fuzz(func(t*testing.T,seedint64) {
45+
key1:=agentrsa.GenerateDeterministicKey(seed)
46+
key2:=agentrsa.GenerateDeterministicKey(seed)
47+
assert.Equal(t,key1,key2)
48+
assert.EqualExportedValues(t,key1,key2)
49+
})
50+
}

‎agent/agentssh/agentssh.go

Lines changed: 2 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@ package agentssh
33
import (
44
"bufio"
55
"context"
6-
"crypto/rsa"
76
"errors"
87
"fmt"
98
"io"
10-
"math/big"
11-
"math/rand"
129
"net"
1310
"os"
1411
"os/exec"
@@ -33,6 +30,7 @@ import (
3330
"cdr.dev/slog"
3431

3532
"github.com/coder/coder/v2/agent/agentexec"
33+
"github.com/coder/coder/v2/agent/agentrsa"
3634
"github.com/coder/coder/v2/agent/usershell"
3735
"github.com/coder/coder/v2/codersdk"
3836
"github.com/coder/coder/v2/pty"
@@ -1120,75 +1118,7 @@ func CoderSigner(seed int64) (gossh.Signer, error) {
11201118
// Clients should ignore the host key when connecting.
11211119
// The agent needs to authenticate with coderd to SSH,
11221120
// so SSH authentication doesn't improve security.
1123-
1124-
// Since the standard lib purposefully does not generate
1125-
// deterministic rsa keys, we need to do it ourselves.
1126-
coderHostKey:=func()*rsa.PrivateKey {
1127-
// Create deterministic random source
1128-
// nolint: gosec
1129-
deterministicRand:=rand.New(rand.NewSource(seed))
1130-
1131-
// Use fixed values for p and q based on the seed
1132-
p:=big.NewInt(0)
1133-
q:=big.NewInt(0)
1134-
e:=big.NewInt(65537)// Standard RSA public exponent
1135-
1136-
// Generate deterministic primes using the seeded random
1137-
// Each prime should be ~1024 bits to get a 2048-bit key
1138-
for {
1139-
p.SetBit(p,1024,1)// Ensure it's large enough
1140-
fori:=0;i<1024;i++ {
1141-
ifdeterministicRand.Int63()%2==1 {
1142-
p.SetBit(p,i,1)
1143-
}else {
1144-
p.SetBit(p,i,0)
1145-
}
1146-
}
1147-
ifp.ProbablyPrime(20) {
1148-
break
1149-
}
1150-
}
1151-
1152-
for {
1153-
q.SetBit(q,1024,1)// Ensure it's large enough
1154-
fori:=0;i<1024;i++ {
1155-
ifdeterministicRand.Int63()%2==1 {
1156-
q.SetBit(q,i,1)
1157-
}else {
1158-
q.SetBit(q,i,0)
1159-
}
1160-
}
1161-
ifq.ProbablyPrime(20)&&p.Cmp(q)!=0 {
1162-
break
1163-
}
1164-
}
1165-
1166-
// Calculate n = p * q
1167-
n:=new(big.Int).Mul(p,q)
1168-
1169-
// Calculate phi = (p-1) * (q-1)
1170-
p1:=new(big.Int).Sub(p,big.NewInt(1))
1171-
q1:=new(big.Int).Sub(q,big.NewInt(1))
1172-
phi:=new(big.Int).Mul(p1,q1)
1173-
1174-
// Calculate private exponent d
1175-
d:=new(big.Int).ModInverse(e,phi)
1176-
1177-
// Create the private key
1178-
privateKey:=&rsa.PrivateKey{
1179-
PublicKey: rsa.PublicKey{
1180-
N:n,
1181-
E:int(e.Int64()),
1182-
},
1183-
D:d,
1184-
Primes: []*big.Int{p,q},
1185-
}
1186-
1187-
// Compute precomputed values
1188-
privateKey.Precompute()
1189-
1190-
returnprivateKey
1191-
}()
1121+
coderHostKey:=agentrsa.GenerateDeterministicKey(seed)
11921122

11931123
coderSigner,err:=gossh.NewSignerFromKey(coderHostKey)
11941124
returncoderSigner,err

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp